code:
/*
 * ExprSynth.cpp - Implementation of a Frontend to ExprTk
 *
 * Copyright (c) 2016-2017 Orr Dvori
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "ExprSynth.h"

#include <string>
#include <vector>
#include <math.h>
#include <cstdlib>
#include <random>

#include "Xpressive.h"

#include "interpolation.h"
#include "lmms_math.h"
#include "NotePlayHandle.h"

#include "exprtk.hpp"

#define WARN_EXPRTK qWarning("ExprTk exception")
typedef exprtk::symbol_table<float> symbol_table_t;
typedef exprtk::expression<float> expression_t;
typedef exprtk::parser<float> parser_t;

template <typename T,typename Functor,bool optimize>
struct freefunc0 : public exprtk::ifunction<T>
{
	using exprtk::ifunction<T>::operator();

	freefunc0() : exprtk::ifunction<T>(0) {
		if (optimize) { exprtk::disable_has_side_effects(*this); }
	}
	inline T operator()()
	{ return Functor::process(); }
};
template <typename T,typename Functor,bool optimize>
struct freefunc1 : public exprtk::ifunction<T>
{
	using exprtk::ifunction<T>::operator();

	freefunc1() : exprtk::ifunction<T>(1) {
		if (optimize) { exprtk::disable_has_side_effects(*this); }
	}
	inline T operator()(const T& x)
	{ return Functor::process(x); }
};

template <typename T>
struct IntegrateFunction : public exprtk::ifunction<T>
{

	using exprtk::ifunction<T>::operator();
	virtual ~IntegrateFunction()
	{
		delete [] m_counters;
	}

	IntegrateFunction(const unsigned int* frame, unsigned int sample_rate,unsigned int max_counters) :
	exprtk::ifunction<T>(1),
	m_frame(frame),
	m_sample_rate(sample_rate),
	m_max_counters(max_counters),
	m_nCounters(0),
	m_nCountersCalls(0),
	m_cc(0)
	{
		m_counters=new double[max_counters];
		clearArray(m_counters,max_counters);
	}

	inline T operator()(const T& x)
	{
		if (*m_frame == 0)
		{
			++m_nCountersCalls;
			if (m_nCountersCalls > m_max_counters)
			{
				return 0;
			}
			m_cc = m_nCounters;
			++m_nCounters;
		}

		T res = 0;
		if (m_cc < m_nCounters)
		{
			res = m_counters[m_cc];
			m_counters[m_cc] += x;
		}
		m_cc = (m_cc + 1) % m_nCountersCalls;
		return res / m_sample_rate;
	}

	const unsigned int* const m_frame;
	const unsigned int m_sample_rate;
	const unsigned int m_max_counters;
	unsigned int m_nCounters;
	unsigned int m_nCountersCalls;
	unsigned int m_cc;
	double *m_counters;
};

template <typename T>
struct LastSampleFunction : public exprtk::ifunction<T>
{

	using exprtk::ifunction<T>::operator();
	virtual ~LastSampleFunction()
	{
		delete [] m_samples;
	}

	LastSampleFunction(unsigned int history_size) :
	exprtk::ifunction<T>(1),
	m_history_size(history_size),
	m_pivot_last(history_size - 1)
	{
		m_samples = new T[history_size];
		clearArray(m_samples, history_size);
	}

	inline T operator()(const T& x)
	{
		if (!std::isnan(x) && !std::isinf(x))
		{
			const int ix=(int)x;
			if (ix>=1 && ix<=m_history_size)
			{
				return m_samples[(ix + m_pivot_last) % m_history_size];
			}
		}
		return 0;
	}
	void setLastSample(const T& sample)
	{
		if (!std::isnan(sample) && !std::isinf(sample))
		{
			m_samples[m_pivot_last] = sample;
		}
		if (m_pivot_last == 0)
		{
			m_pivot_last = m_history_size - 1;
		}
		else {
			--m_pivot_last;
		}
	}
	unsigned int m_history_size;
	unsigned int m_pivot_last;
	T *m_samples;
};

template <typename T>
struct WaveValueFunction : public exprtk::ifunction<T>
{
	using exprtk::ifunction<T>::operator();

	WaveValueFunction(const T* v, std::size_t s)
	: exprtk::ifunction<T>(1),
	m_vec(v),
	m_size(s)
	{}

	inline T operator()(const T& index)
	{
		return m_vec[(int) ( positiveFraction(index) * m_size )];
	}
	const T *m_vec;
	const std::size_t m_size;
};
template <typename T>
struct WaveValueFunctionInterpolate : public exprtk::ifunction<T>
{
	using exprtk::ifunction<T>::operator();

	WaveValueFunctionInterpolate(const T* v, std::size_t s)
	: exprtk::ifunction<T>(1),
	m_vec(v),
	m_size(s)
	{}

	inline T operator()(const T& index)
	{
		const T x = positiveFraction(index) * m_size;
		const int ix = (int)x;
		const float xfrc = fraction(x);
		return linearInterpolate(m_vec[ix], m_vec[(ix + 1) % m_size], xfrc);
	}
	const T *m_vec;
	const std::size_t m_size;
};
static const unsigned int random_data[257]={
0xd76a33ec, 0x4a767724, 0xb34ebd08 ,0xf4024196,
0x17b426e2, 0x8dc6389a, 0x1b5dcb93 ,0xa771bd3f,
0x078d502e, 0x8980988a, 0x1f64f846 ,0xb5b48ed7,
0xf0742cfb, 0xe7c66303, 0xc9472876 ,0x6c7494a5,
0x5c2203a1, 0x23986344, 0x7d344fa0 ,0x4f39474a,
0x28ac8b2b, 0x10f779b2, 0x6e79e659 ,0x32e44c52,
0xf790aa55, 0x98b05083, 0xb5d44f1c ,0xe553da04,
0xa884c6d2, 0x43274953, 0xbcb57404 ,0x43f7d32a,
0xf1890f8b, 0x019f4dce, 0x5c4ede33 ,0x2dec1a7e,
0x0f3eab09, 0x2197c93c, 0xae933f42 ,0x80d4b111,
0x6e5bd30a, 0x17139c70, 0xe15f7eb0 ,0x1798f893,
0xe1c6be1c, 0xe21edf9b, 0x4702e081 ,0x8a2cb85a,
0xbf3c1f15, 0x147f4685, 0x9221d731 ,0x3c7580f3,
0xc1c08498, 0x8e198b35, 0xf821c15a ,0x4d3cd2d4,
0xad89a3b7, 0xd48f915f, 0xcaf893f0 ,0xa64a4b8e,
0x20715f54, 0x1ba4de0a, 0x17ac6e91 ,0xd82ea8c0,
0x638a0ba5, 0xe7a76c0f, 0x486c5476 ,0x334bbd0a,
0xffe29c55, 0x7247efaf, 0x15f98e83 ,0x7a4a79ac,
0x350cd175, 0xc7107908, 0xa85c67f7 ,0x9c5002c4,
0x3cf27d2c, 0x314d8450, 0x05552886 ,0x87a73642,
0x827832e4, 0x9412cc67, 0x261979e6 ,0xb31da27f,
0x3e6bbafb, 0x663f1968, 0xd84274e2, 0xdd91d982,
0xd25c4805, 0x9567f860, 0xab99675c, 0x2254733b,
0x18799dd7, 0xee328916, 0xb9419a1b, 0x01b7a66f,
0xbcdc05e1, 0x788de4ae, 0x366e77cf, 0x81a1ebd2,
0x97be859a, 0x17d4b533, 0x22dab3a9, 0xc99871ea,
0xc7502c91, 0x4474b65f, 0x655d059d, 0x0ddc1348,
0x8325909b, 0x4873c155, 0x9fa30438, 0x7250b7a8,
0x90db2715, 0xf65e1cef, 0x41b74cf4, 0x38fba01c,
0xe9eefb40, 0x9e5524ea, 0x1d3fc077, 0x04ec39db,
0x1c0d501c, 0xb93f26d9, 0xf9f910b9, 0x806fce99,
0x5691ffdf, 0x1e63b27a, 0xf2035d42, 0xd3218a0b,
0x12eae6db, 0xeba372a9, 0x6f975260, 0xc514ae91,
0xebddb8ad, 0xc53207c0, 0xdbda57dc, 0x8fb38ef4,
0xfaa4f1bc, 0x40caf49f, 0xcb394b41, 0x424fc698,
0xb79a9ece, 0x331202d6, 0xc604ed4d, 0x5e85819f,
0x67222eda, 0xd976ba71, 0x7d083ec6, 0x040c819e,
0xc762c934, 0xa6684333, 0x2eaccc54, 0x69dc04b9,
0x0499cf36, 0x6351f438, 0x6db2dc34, 0x787ae036,
0x11b5c6ac, 0x552b7227, 0x32a4c993, 0xf7f4c49d,
0x7ac9e2d9, 0xf3d32020, 0x4ff01f89, 0x6f0e60bb,
0x3c6ed445, 0x7ca01986, 0x96901ecf, 0xe10df188,
0x62a6da6d, 0x8deee09f, 0x5347cb66, 0x5249f452,
0x22704d4d, 0x6221555f, 0x6aa0ea90, 0xe1f7bae3,
0xd106626f, 0x6365a9db, 0x1989bb81, 0xfc2daa73,
0x303c60b3, 0xcd867baa, 0x7c5787c2, 0x60082b30,
0xa68d3a81, 0x15a10f5d, 0x81b21c8a, 0x4bfb82e2,
0xff01c176, 0xff3c8b65, 0x8cc6bd29, 0xc678d6ff,
0x99b86508, 0x3c47e314, 0x766ecc05, 0xba186cb0,
0x42f57199, 0x5ef524f4, 0xb8419750, 0x6ae2a9d0,
0x291eaa18, 0x4e64b189, 0x506eb1d3, 0x78361d46,
0x6a2fcb7e, 0xbc0a46de, 0xb557badf, 0xad3de958,
0xa2901279, 0x491decbf, 0x257383df, 0x94dd19d1,
0xd0cfbbe2, 0x9063d36d, 0x81e44c3b, 0x973e9cc9,
0xfbe34690, 0x4eee3034, 0x1c413676, 0xf6735b8f,
0xf2991aca, 0x0ec85159, 0x6ce00ade, 0xad49de57,
0x025edf30, 0x42722b67, 0x30cfa6b2, 0x32df8676,
0x387d4500, 0x97fa67fd, 0x027c994a, 0x77c71d0c,
0x478eb75a, 0x898370a6, 0x73e7cca3, 0x34ace0ad,
0xc8ecb388, 0x5375c3aa, 0x9c194d87, 0x1b65246d,
0xca000bcf, 0x8a0fb13d, 0x81b957b0, 0xac627bfb,
0xc0fe47e5, 0xf3db0ad8, 0x1c605c7d, 0x5f579884,
0x63e079b5, 0x3d96f7cf, 0x3edd46e9, 0xc347c61e,
0x7b2b2a0e, 0x63dfcf51, 0x596781dd, 0x80304c4d,
0xa66f8b47
};

inline unsigned int rotateLeft(unsigned int x, const int b)
{
	if (b > -32 && b < 32 && b != 0)
	{
		if (b < 0)
		{
			x= ( x >> (-b) ) | ( x << (32 + b) );
		}
		else
		{
			x= ( x << b ) | ( x >> (32 - b) );
		}
	}
	return x;
}

struct RandomVectorSeedFunction : public exprtk::ifunction<float>
{
	using exprtk::ifunction<float>::operator();

	RandomVectorSeedFunction() :
	exprtk::ifunction<float>(2)
	{ exprtk::disable_has_side_effects(*this); }

	static inline float randv(const float& index,int irseed)
	{
		if (index < 0 || std::isnan(index) || std::isinf(index))
		{
			return 0;
		}
		const unsigned int xi = (unsigned int)index;
		const unsigned int si = irseed % data_size;
		const unsigned int sa = irseed / data_size;
		unsigned int res=rotateLeft(random_data[(xi + 23 * si + 1) % data_size] ^ random_data[(xi / data_size + sa) % data_size],sa % 31 + 1);
		res ^= rotateLeft(random_data[(3 * xi + si + 13) % data_size],(xi+2*si) % 32) ^rotateLeft( random_data[(xi / data_size + 2 * sa) % data_size],xi % 31 + 1);
		return static_cast<int>(res) / (float)(1 << 31);
	}

	inline float operator()(const float& index,const float& seed)
	{
		int irseed;
		if (seed < 0 || std::isnan(seed) || std::isinf(seed))
		{
			irseed=0;
		}
		else
			irseed=(int)seed;
		return randv(index,irseed);
	}

	static const int data_size=sizeof(random_data)/sizeof(int);
};
static RandomVectorSeedFunction randsv_func;

struct RandomVectorFunction : public exprtk::ifunction<float>
{
	using exprtk::ifunction<float>::operator();

	RandomVectorFunction(const unsigned int seed) :
	exprtk::ifunction<float>(1),
	m_rseed(seed)
	{ exprtk::disable_has_side_effects(*this); }

	inline float operator()(const float& index)
	{
		return RandomVectorSeedFunction::randv(index,m_rseed);
	}

	const unsigned int m_rseed;
};

namespace SimpleRandom {
	std::mt19937 generator (17);  // mt19937 is a standard mersenne_twister_engine
	std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
	struct float_random_with_engine
	{
		static inline float process()
		{
			return dist(generator);
		}
	};
}

static freefunc0<float,SimpleRandom::float_random_with_engine,false> simple_rand;

class ExprFrontData
{
public:
	ExprFrontData(int last_func_samples):
	m_rand_vec(SimpleRandom::generator()),
	m_integ_func(NULL),
	m_last_func(last_func_samples)
	{}
	~ExprFrontData()
	{
		for (int i = 0; i < m_cyclics.size() ; ++i)
		{
			delete m_cyclics[i];
		}
		for (int i = 0; i < m_cyclics_interp.size() ; ++i)
		{
			delete m_cyclics_interp[i];
		}
		if (m_integ_func)
		{
			delete m_integ_func;
		}
	}

	symbol_table_t m_symbol_table;
	expression_t m_expression;
	std::string m_expression_string;
	std::vector<WaveValueFunction<float>* > m_cyclics;
	std::vector<WaveValueFunctionInterpolate<float>* > m_cyclics_interp;
	RandomVectorFunction m_rand_vec;
	IntegrateFunction<float> *m_integ_func;
	LastSampleFunction<float> m_last_func;

};

struct sin_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		return sinf(x * F_2PI);
	}
};
static freefunc1<float,sin_wave,true> sin_wave_func;
struct square_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		if (x >= 0.5f) { return -1.0f; }
		else { return 1.0f; }
	}
};
static freefunc1<float,square_wave,true> square_wave_func;
struct triangle_wave
{
	static inline float process(float x)
	{
		x=positiveFraction(x);
		if (x < 0.25f)
		{
			return x * 4.0f;
		}
		else
		{
			if (x < 0.75f) { return 2.0f - x * 4.0f; }

			else { return x * 4.0f - 4.0f; }
		}
	}
};
static freefunc1<float,triangle_wave,true> triangle_wave_func;
struct saw_wave
{
	static inline float process(float x)
	{
		x=positiveFraction(x);
		return 2.0f * x - 1.0f;
	}
};
static freefunc1<float,saw_wave,true> saw_wave_func;
struct moogsaw_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		if( x < 0.5f )
		{
			return -1.0f + x * 4.0f;
		}
		return 1.0f - 2.0f * x;
	}
};
static freefunc1<float,moogsaw_wave,true> moogsaw_wave_func;
struct moog_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		if( x > 0.5f )
		{
			x = 1.0f - x;
			return -1.0f + 4.0f * x * x;
		}
		return -1.0f + 8.0f * x * x;
	}
};
static freefunc1<float,moog_wave,true> moog_wave_func;
struct exp_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		if( x > 0.5f )
		{
			x = 1.0f - x;
		}
		return -1.0f + 8.0f * x * x;
	}
};
static freefunc1<float,exp_wave,true> exp_wave_func;
struct exp2_wave
{
	static inline float process(float x)
	{
		x = positiveFraction(x);
		if( x > 0.5f )
		{
			return -1.0f + 8.0f * (1.0f-x) * x;
		}
		return -1.0f + 8.0f * x * x;
	}
};
static freefunc1<float,exp2_wave,true> exp2_wave_func;
struct harmonic_cent
{
	static inline float process(float x)
	{
		return powf(2, x / 1200);
	}
};
static freefunc1<float,harmonic_cent,true> harmonic_cent_func;
struct harmonic_semitone
{
	static inline float process(float x)
	{
		return powf(2, x / 12);
	}
};
static freefunc1<float,harmonic_semitone,true> harmonic_semitone_func;

ExprFront::ExprFront(const char * expr, int last_func_samples)
{
	m_valid = false;
	try
	{
		m_data = new ExprFrontData(last_func_samples);

		m_data->m_expression_string = expr;
		m_data->m_symbol_table.add_pi();

		m_data->m_symbol_table.add_constant("e", F_E);

		m_data->m_symbol_table.add_constant("seed", SimpleRandom::generator() & max_float_integer_mask);

		m_data->m_symbol_table.add_function("sinew", sin_wave_func);
		m_data->m_symbol_table.add_function("squarew", square_wave_func);
		m_data->m_symbol_table.add_function("trianglew", triangle_wave_func);
		m_data->m_symbol_table.add_function("saww", saw_wave_func);
		m_data->m_symbol_table.add_function("moogsaww", moogsaw_wave_func);
		m_data->m_symbol_table.add_function("moogw", moog_wave_func);
		m_data->m_symbol_table.add_function("expw", exp_wave_func);
		m_data->m_symbol_table.add_function("expnw", exp2_wave_func);
		m_data->m_symbol_table.add_function("cent", harmonic_cent_func);
		m_data->m_symbol_table.add_function("semitone", harmonic_semitone_func);
		m_data->m_symbol_table.add_function("rand", simple_rand);
		m_data->m_symbol_table.add_function("randv", m_data->m_rand_vec);
		m_data->m_symbol_table.add_function("randsv", randsv_func);
		m_data->m_symbol_table.add_function("last", m_data->m_last_func);
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
}
ExprFront::~ExprFront()
{
	try
	{
		delete m_data;
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
}

bool ExprFront::compile()
{
	m_valid = false;
	try
	{
		m_data->m_expression.register_symbol_table(m_data->m_symbol_table);
		parser_t::settings_store sstore;
		sstore.disable_all_logic_ops();
		sstore.disable_all_assignment_ops();
		sstore.disable_all_control_structures();
		parser_t parser(sstore);

		m_valid=parser.compile(m_data->m_expression_string, m_data->m_expression);
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
	return m_valid;
}
float ExprFront::evaluate()
{
	try
	{
		if (!m_valid) return 0;
		float res = m_data->m_expression.value();
		m_data->m_last_func.setLastSample(res);
		return res;
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
	return 0;

}
bool ExprFront::add_variable(const char* name, float& ref)
{
	try
	{
		return m_data->m_symbol_table.add_variable(name, ref);
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
	return false;
}

bool ExprFront::add_constant(const char* name, float ref)
{
	try
	{
		return m_data->m_symbol_table.add_constant(name, ref);
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
	return false;
}

bool ExprFront::add_cyclic_vector(const char* name, const float* data, size_t length, bool interp)
{
	try
	{
		if (interp)
		{
			WaveValueFunctionInterpolate<float> *wvf = new WaveValueFunctionInterpolate<float>(data, length);
			m_data->m_cyclics_interp.push_back(wvf);
			return m_data->m_symbol_table.add_function(name, *wvf);
		}
		else
		{
			WaveValueFunction<float> *wvf = new WaveValueFunction<float>(data, length);
			m_data->m_cyclics.push_back(wvf);
			return m_data->m_symbol_table.add_function(name, *wvf);
		}
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
	return false;
}
size_t find_occurances(const std::string& haystack, const char* const needle)
{
	size_t last_pos = 0;
	size_t count = 0;
	const size_t len = strlen(needle);
	if (len > 0)
	{
		while (last_pos + len <= haystack.length())
		{
			last_pos = haystack.find(needle, last_pos);
			if (last_pos == std::string::npos)
				break;
			++count;
			last_pos += len;
		}
	}
	return count;
}

void ExprFront::setIntegrate(const unsigned int* const frameCounter, const unsigned int sample_rate)
{
	if (m_data->m_integ_func == NULL)
	{
		const unsigned int ointeg = find_occurances(m_data->m_expression_string,"integrate");
		if ( ointeg > 0 )
		{
			m_data->m_integ_func = new IntegrateFunction<float>(frameCounter,sample_rate,ointeg);
			try
			{
				m_data->m_symbol_table.add_function("integrate",*m_data->m_integ_func);
			}
			catch(...)
			{
				WARN_EXPRTK;
			}
		}
	}
}

ExprSynth::ExprSynth(const WaveSample *gW1, const WaveSample *gW2, const WaveSample *gW3,
	ExprFront *exprO1, ExprFront *exprO2,
	NotePlayHandle *nph, const sample_rate_t sample_rate,
	const FloatModel* pan1, const FloatModel* pan2, float rel_trans):
	m_exprO1(exprO1),
	m_exprO2(exprO2),
	m_W1(gW1),
	m_W2(gW2),
	m_W3(gW3),
	m_nph(nph),
	m_sample_rate(sample_rate),
	m_pan1(pan1),
	m_pan2(pan2),
	m_rel_transition(rel_trans)
{
	m_note_sample = 0;
	m_note_rel_sample = 0;
	m_note_rel_sec = 0;
	m_note_sample_sec = 0;
	m_released = 0;
	m_frequency = m_nph->frequency();
	m_rel_inc = 1000.0 / (m_sample_rate * m_rel_transition);//rel_transition in ms. compute how much increment in each frame

	auto init_expression_step2 = [this](ExprFront * e) {
		e->add_cyclic_vector("W1", m_W1->m_samples,m_W1->m_length, m_W1->m_interpolate);
		e->add_cyclic_vector("W2", m_W2->m_samples,m_W2->m_length, m_W2->m_interpolate);
		e->add_cyclic_vector("W3", m_W3->m_samples,m_W3->m_length, m_W3->m_interpolate);
		e->add_variable("t", m_note_sample_sec);
		e->add_variable("f", m_frequency);
		e->add_variable("rel",m_released);
		e->add_variable("trel",m_note_rel_sec);
		e->setIntegrate(&m_note_sample,m_sample_rate);
		e->compile();
	};
	init_expression_step2(m_exprO1);
	init_expression_step2(m_exprO2);

}

ExprSynth::~ExprSynth()
{
	if (m_exprO1)
	{
		delete m_exprO1;
	}
	if (m_exprO2)
	{
		delete m_exprO2;
	}
}

void ExprSynth::renderOutput(fpp_t frames, sampleFrame *buf)
{
	try
	{
		bool o1_valid = m_exprO1->isValid();
		bool o2_valid = m_exprO2->isValid();
		if (!o1_valid && !o2_valid)
		{
			return;
		}
		float o1 = 0, o2 = 0;
		float pn1 = m_pan1->value() * 0.5;
		float pn2 = m_pan2->value() * 0.5;
		const float new_freq = m_nph->frequency();
		const float freq_inc = (new_freq - m_frequency) / frames;
		const bool is_released = m_nph->isReleased();

		expression_t *o1_rawExpr = &(m_exprO1->getData()->m_expression);
		expression_t *o2_rawExpr = &(m_exprO2->getData()->m_expression);
		LastSampleFunction<float> * last_func1 = &m_exprO1->getData()->m_last_func;
		LastSampleFunction<float> * last_func2 = &m_exprO2->getData()->m_last_func;
		if (is_released && m_note_rel_sample == 0)
		{
			m_note_rel_sample = m_note_sample;
		}
		if (o1_valid && o2_valid)
		{
			for (fpp_t frame = 0; frame < frames ; ++frame)
			{
				if (is_released && m_released < 1)
				{
					m_released = fmin(m_released+m_rel_inc, 1);
				}
				o1 = o1_rawExpr->value();
				o2 = o2_rawExpr->value();
				last_func1->setLastSample(o1);//put result in the circular buffer for the "last" function.
				last_func2->setLastSample(o2);
				buf[frame][0] = (-pn1 + 0.5) * o1 + (-pn2 + 0.5) * o2;
				buf[frame][1] = ( pn1 + 0.5) * o1 + ( pn2 + 0.5) * o2;
				m_note_sample++;
				m_note_sample_sec = m_note_sample / (float)m_sample_rate;
				if (is_released)
				{
					m_note_rel_sec = (m_note_sample - m_note_rel_sample) / (float)m_sample_rate;
				}
				m_frequency += freq_inc;
			}
		}
		else
		{

			if (o2_valid)
			{
				o1_rawExpr = o2_rawExpr;
				last_func1 = last_func2;
				pn1 = pn2;
			}
			for (fpp_t frame = 0; frame < frames ; ++frame)
			{
				if (is_released && m_released < 1)
				{
					m_released = fmin(m_released+m_rel_inc, 1);
				}
				o1 = o1_rawExpr->value();
				last_func1->setLastSample(o1);
				buf[frame][0] = (-pn1 + 0.5) * o1;
				buf[frame][1] = ( pn1 + 0.5) * o1;
				m_note_sample++;
				m_note_sample_sec = m_note_sample / (float)m_sample_rate;
				if (is_released)
				{
					m_note_rel_sec = (m_note_sample - m_note_rel_sample) / (float)m_sample_rate;
				}
				m_frequency += freq_inc;
			}
		}
		m_frequency = new_freq;
	}
	catch(...)
	{
		WARN_EXPRTK;
	}
}


code:
/*
 * exprfront.h - header file to a Frontend to ExprTk
 *
 * Copyright (c) 2016-2017 Orr Dvori
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef EXPRSYNTH_H
#define EXPRSYNTH_H

#include <cmath>
#include <cstddef>
#include <limits>
#include "AutomatableModel.h"
#include "Graph.h"
#include "Instrument.h"
#include "MemoryManager.h"

class ExprFrontData;

class ExprFront
{
public:
	typedef float (*ff1data_functor)(void*, float);
	ExprFront(const char* expr, int last_func_samples);
	~ExprFront();
	bool compile();
	inline bool isValid() { return m_valid; }
	float evaluate();
	bool add_variable(const char* name, float & ref);
	bool add_constant(const char* name, float  ref);
	bool add_cyclic_vector(const char* name, const float* data, size_t length, bool interp = false);
	void setIntegrate(const unsigned int* frameCounter, unsigned int sample_rate);
	ExprFrontData* getData() { return m_data; }
private:
	ExprFrontData *m_data;
	bool m_valid;

	static const int max_float_integer_mask=(1<<(std::numeric_limits<float>::digits))-1;

};

class WaveSample
{
public:
	WaveSample(int length)
	{
		m_length = length;
		m_samples = new float[m_length];
		for(int i = 0 ; i < m_length ; ++i)
			m_samples[i] = 0;
	}
	WaveSample(const graphModel * graph)
	{
		m_length = graph->length();
		m_samples = new float[m_length];
		memcpy(m_samples, graph->samples(), m_length * sizeof(float));
	}
	inline void copyFrom(const graphModel * graph)
	{
		memcpy(m_samples, graph->samples(), m_length * sizeof(float));
	}
	~WaveSample()
	{
		delete [] m_samples;
	}
	inline void setInterpolate(bool _interpolate) { m_interpolate = _interpolate; }
	float *m_samples;
	int m_length;
	bool m_interpolate;
};

class ExprSynth
{
	MM_OPERATORS
public:
	ExprSynth(const WaveSample* gW1, const WaveSample* gW2, const WaveSample* gW3, ExprFront* exprO1, ExprFront* exprO2, NotePlayHandle* nph,
			const sample_rate_t sample_rate, const FloatModel* pan1, const FloatModel* pan2, float rel_trans);
	virtual ~ExprSynth();

	void renderOutput(fpp_t frames, sampleFrame* buf );

private:
	ExprFront *m_exprO1, *m_exprO2;
	const WaveSample *m_W1, *m_W2, *m_W3;
	unsigned int m_note_sample;
	unsigned int m_note_rel_sample;
	float m_note_sample_sec;
	float m_note_rel_sec;
	float m_frequency;
	float m_released;
	NotePlayHandle* m_nph;
	const sample_rate_t m_sample_rate;
	const FloatModel *m_pan1,*m_pan2;
	float m_rel_transition;
	float m_rel_inc;

} ;

inline float positiveFraction(float x)
{
	if (std::isnan(x) || std::isinf(x))
		return 0;
	if (x<0)
	{
		x+=static_cast<int>(1-x);
	}
	return x-static_cast<int>(x);
}

template <typename T>
inline void clearArray(T* arr,unsigned int size)
{
	const T* const arr_end = arr + size;
	while(arr < arr_end)
	{
		*arr=0;
		++arr;
	}
}

#endif

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * Xpressive.cpp - Instrument which uses a mathematical formula parser
 *
 * Copyright (c) 2016-2017 Orr Dvori
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "Xpressive.h"

#include <QDomElement>

#include "Engine.h"
#include "Graph.h"
#include "GuiApplication.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "LedCheckbox.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "NotePlayHandle.h"
#include "Oscillator.h"
#include "PixmapButton.h"
#include "Song.h"
#include "SubWindow.h"
#include "ToolTip.h"

#include "base64.h"
#include "lmms_constants.h"

#include "embed.h"

#include "ExprSynth.h"

#include "plugin_export.h"

extern "C" {

Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY(
	PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser",
	"Mathematical expression parser"), "Orr Dvori", 0x0100,
	Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL };

}

/*
 * nice test:
O1 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t+0.5))+sinew(t*f)*(0.5+0.5sinew(12*A1*t))
O2 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t))+sinew(t*f)*(0.5+0.5sinew(12*A1*t+0.5))
*/

/***********************************************************************
 *
 *	class Xpressive
 *
 *	lmms - plugin
 *
 ***********************************************************************/
#define GRAPH_LENGTH 4096

Xpressive::Xpressive(InstrumentTrack* instrument_track) :
	Instrument(instrument_track, &xpressive_plugin_descriptor),
	m_graphO1(-1.0f, 1.0f, 360, this),
	m_graphO2(-1.0f, 1.0f, 360, this),
	m_graphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_graphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_graphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_rawgraphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_rawgraphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_rawgraphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
	m_selectedGraph(0, 0, 6, this, tr("Selected graph")),
	m_parameterA1(1, -1.0f, 1.0f, 0.01f, this, tr("A1")),
	m_parameterA2(1, -1.0f, 1.0f, 0.01f, this, tr("A2")),
	m_parameterA3(1, -1.0f, 1.0f, 0.01f, this, tr("A3")),
	m_smoothW1(0, 0.0f, 70.0f, 1.0f, this, tr("W1 smoothing")),
	m_smoothW2(0, 0.0f, 70.0f, 1.0f, this, tr("W2 smoothing")),
	m_smoothW3(0, 0.0f, 70.0f, 1.0f, this, tr("W3 smoothing")),
	m_interpolateW1(false, this),
	m_interpolateW2(false, this),
	m_interpolateW3(false, this),
	m_panning1( 1, -1.0f, 1.0f, 0.01f, this, tr("Panning 1")),
	m_panning2(-1, -1.0f, 1.0f, 0.01f, this, tr("Panning 2")),
	m_relTransition(50.0f, 0.0f, 500.0f, 1.0f, this, tr("Rel trans")),
	m_W1(GRAPH_LENGTH),
	m_W2(GRAPH_LENGTH),
	m_W3(GRAPH_LENGTH),
	m_exprValid(false, this)
{
	m_outputExpression[0]="sinew(integrate(f*(1+0.05sinew(12t))))*(2^(-(1.1+A2)*t)*(0.4+0.1(1+A3)+0.4sinew((2.5+2A1)t))^2)";
	m_outputExpression[1]="expw(integrate(f*atan(500t)*2/pi))*0.5+0.12";
}

Xpressive::~Xpressive() {
}

void Xpressive::saveSettings(QDomDocument & _doc, QDomElement & _this) {

	// Save plugin version
	_this.setAttribute("version", "0.1");
	_this.setAttribute("O1", QString(m_outputExpression[0]));
	_this.setAttribute("O2", QString(m_outputExpression[1]));
	_this.setAttribute("W1", QString(m_wavesExpression[0]));
	// Save sample shape base64-encoded
	QString sampleString;
	base64::encode( (const char*)m_rawgraphW1.samples(),
		 m_rawgraphW1.length() * sizeof(float), sampleString );
	_this.setAttribute( "W1sample", sampleString );

	_this.setAttribute("W2", QString(m_wavesExpression[1]));
	base64::encode( (const char*)m_rawgraphW2.samples(),
		 m_rawgraphW2.length() * sizeof(float), sampleString );
	_this.setAttribute( "W2sample", sampleString );
	_this.setAttribute("W3", QString(m_wavesExpression[2]));
	base64::encode( (const char*)m_rawgraphW3.samples(),
		 m_rawgraphW3.length() * sizeof(float), sampleString );
	_this.setAttribute( "W3sample", sampleString );
	m_smoothW1.saveSettings(_doc,_this,"smoothW1");
	m_smoothW2.saveSettings(_doc,_this,"smoothW2");
	m_smoothW3.saveSettings(_doc,_this,"smoothW3");
	m_interpolateW1.saveSettings(_doc,_this,"interpolateW1");
	m_interpolateW2.saveSettings(_doc,_this,"interpolateW2");
	m_interpolateW3.saveSettings(_doc,_this,"interpolateW3");
	m_parameterA1.saveSettings(_doc,_this,"A1");
	m_parameterA2.saveSettings(_doc,_this,"A2");
	m_parameterA3.saveSettings(_doc,_this,"A3");
	m_panning1.saveSettings(_doc,_this,"PAN1");
	m_panning2.saveSettings(_doc,_this,"PAN2");
	m_relTransition.saveSettings(_doc,_this,"RELTRANS");

}

void Xpressive::loadSettings(const QDomElement & _this) {

	m_outputExpression[0]=_this.attribute( "O1").toLatin1();
	m_outputExpression[1]=_this.attribute( "O2").toLatin1();
	m_wavesExpression[0]=_this.attribute( "W1").toLatin1();
	m_wavesExpression[1]=_this.attribute( "W2").toLatin1();
	m_wavesExpression[2]=_this.attribute( "W3").toLatin1();

	m_smoothW1.loadSettings(_this,"smoothW1");
	m_smoothW2.loadSettings(_this,"smoothW2");
	m_smoothW3.loadSettings(_this,"smoothW3");
	m_interpolateW1.loadSettings(_this,"interpolateW1");
	m_interpolateW2.loadSettings(_this,"interpolateW2");
	m_interpolateW3.loadSettings(_this,"interpolateW3");
	m_parameterA1.loadSettings(_this,"A1");
	m_parameterA2.loadSettings(_this,"A2");
	m_parameterA3.loadSettings(_this,"A3");
	m_panning1.loadSettings(_this,"PAN1");
	m_panning2.loadSettings(_this,"PAN2");
	m_relTransition.loadSettings(_this,"RELTRANS");

	int size = 0;
	char * dst = 0;
	base64::decode( _this.attribute( "W1sample"), &dst, &size );

	m_rawgraphW1.setSamples( (float*) dst );
	delete[] dst;
	base64::decode( _this.attribute( "W2sample"), &dst, &size );

	m_rawgraphW2.setSamples( (float*) dst );
	delete[] dst;
	base64::decode( _this.attribute( "W3sample"), &dst, &size );

	m_rawgraphW3.setSamples( (float*) dst );
	delete[] dst;

	smooth(m_smoothW1.value(),&m_rawgraphW1,&m_graphW1);
	smooth(m_smoothW2.value(),&m_rawgraphW2,&m_graphW2);
	smooth(m_smoothW3.value(),&m_rawgraphW3,&m_graphW3);
	m_W1.copyFrom(&m_graphW1);
	m_W2.copyFrom(&m_graphW2);
	m_W3.copyFrom(&m_graphW3);
}

QString Xpressive::nodeName() const {
	return (xpressive_plugin_descriptor.name);
}

void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) {
	m_A1=m_parameterA1.value();
	m_A2=m_parameterA2.value();
	m_A3=m_parameterA3.value();

	if (nph->totalFramesPlayed() == 0 || nph->m_pluginData == NULL) {

		ExprFront *exprO1 = new ExprFront(m_outputExpression[0].constData(),Engine::mixer()->processingSampleRate());//give the "last" function a whole second
		ExprFront *exprO2 = new ExprFront(m_outputExpression[1].constData(),Engine::mixer()->processingSampleRate());

		auto init_expression_step1 = [this, nph](ExprFront* e) { //lambda function to init exprO1 and exprO2
			//add the constants and the variables to the expression.
			e->add_constant("key", nph->key());//the key that was pressed.
			e->add_constant("bnote", nph->instrumentTrack()->baseNote()); // the base note
			e->add_constant("srate", Engine::mixer()->processingSampleRate());// sample rate of the mixer
			e->add_constant("v", nph->getVolume() / 255.0); //volume of the note.
			e->add_constant("tempo", Engine::getSong()->getTempo());//tempo of the song.
			e->add_variable("A1", m_A1);//A1,A2,A3: general purpose input controls.
			e->add_variable("A2", m_A2);
			e->add_variable("A3", m_A3);
		};
		init_expression_step1(exprO1);
		init_expression_step1(exprO2);

		m_W1.setInterpolate(m_interpolateW1.value());//set interpolation according to the user selection.
		m_W2.setInterpolate(m_interpolateW2.value());
		m_W3.setInterpolate(m_interpolateW3.value());
		nph->m_pluginData = new ExprSynth(&m_W1, &m_W2, &m_W3, exprO1, exprO2, nph,
				Engine::mixer()->processingSampleRate(), &m_panning1, &m_panning2, m_relTransition.value());
	}

	ExprSynth *ps = static_cast<ExprSynth*>(nph->m_pluginData);
	const fpp_t frames = nph->framesLeftForCurrentPeriod();
	const f_cnt_t offset = nph->noteOffset();

	ps->renderOutput(frames, working_buffer + offset);

	instrumentTrack()->processAudioBuffer(working_buffer, frames + offset, nph);
}

void Xpressive::deleteNotePluginData(NotePlayHandle* nph) {
	delete static_cast<ExprSynth *>(nph->m_pluginData);
}

PluginView * Xpressive::instantiateView(QWidget* parent) {
	return (new XpressiveView(this, parent));
}

class XpressiveKnob: public Knob {
public:
	void setStyle()
	{
		setFixedSize(29, 29);
		setCenterPointX(14.5);
		setCenterPointY(14.5);
		setInnerRadius(4);
		setOuterRadius(9);
		setTotalAngle(300.0);
		setLineWidth(3);
	}
	XpressiveKnob(QWidget * _parent, const QString & _name) :
			Knob(knobStyled, _parent,_name) {
		setStyle();
	}
	XpressiveKnob(QWidget * _parent) :
			Knob(knobStyled, _parent) {
		setStyle();
	}

};

XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) :
	InstrumentViewFixedSize(_instrument, _parent)

{
	const int COL_KNOBS = 191;
	const int BASE_START = 2;
	const int ROW_KNOBSA1 = BASE_START;
	const int ROW_KNOBSA2 = BASE_START + 32;
	const int ROW_KNOBSA3 = BASE_START + 64;
	const int ROW_KNOBSP1 = BASE_START + 100;
	const int ROW_KNOBSP2 = BASE_START + 100 + 32;
	const int ROW_KNOBREL = BASE_START + 100 + 64;
	const int ROW_BTN = BASE_START + 85;
	const int ROW_WAVEBTN = BASE_START + 233 - 26;
	const int EXPR_TEXT_Y = BASE_START + 102;
	const int EXPR_TEXT_H = 90;

	setAutoFillBackground(true);
	QPalette pal;

	pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
	setPalette(pal);

	m_graph = new Graph(this, Graph::LinearStyle, 180, 81);
	m_graph->move(3, BASE_START + 1);
	m_graph->setAutoFillBackground(true);
	m_graph->setGraphColor(QColor(255, 255, 255));
	m_graph->setEnabled(false);

	ToolTip::add(m_graph, tr("Draw your own waveform here "
			"by dragging your mouse on this graph."));

	pal = QPalette();
	pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("wavegraph"));
	m_graph->setPalette(pal);

	PixmapButton * m_w1Btn;
	PixmapButton * m_w2Btn;
	PixmapButton * m_w3Btn;
	PixmapButton * m_o1Btn;
	PixmapButton * m_o2Btn;
	PixmapButton * m_helpBtn;

	m_w1Btn = new PixmapButton(this, NULL);
	m_w1Btn->move(3, ROW_BTN);
	m_w1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w1_active"));
	m_w1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w1_inactive"));
	ToolTip::add(m_w1Btn, tr("Select oscillator W1"));

	m_w2Btn = new PixmapButton(this, NULL);
	m_w2Btn->move(26, ROW_BTN);
	m_w2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w2_active"));
	m_w2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w2_inactive"));
	ToolTip::add(m_w2Btn, tr("Select oscillator W2"));

	m_w3Btn = new PixmapButton(this, NULL);
	m_w3Btn->move(49, ROW_BTN);
	m_w3Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w3_active"));
	m_w3Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w3_inactive"));
	ToolTip::add(m_w3Btn, tr("Select oscillator W3"));

	m_o1Btn = new PixmapButton(this, NULL);
	m_o1Btn->move(79, ROW_BTN);
	m_o1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o1_active"));
	m_o1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o1_inactive"));
	ToolTip::add(m_o1Btn, tr("Select output O1"));

	m_o2Btn = new PixmapButton(this, NULL);
	m_o2Btn->move(101, ROW_BTN);
	m_o2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o2_active"));
	m_o2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o2_inactive"));
	ToolTip::add(m_o2Btn, tr("Select output O2"));

	m_helpBtn = new PixmapButton(this, NULL);
	m_helpBtn->move(133, ROW_BTN);
	m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_active"));
	m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_inactive"));
	ToolTip::add(m_helpBtn, tr("Open help window"));

	m_selectedGraphGroup = new automatableButtonGroup(this);
	m_selectedGraphGroup->addButton(m_w1Btn);
	m_selectedGraphGroup->addButton(m_w2Btn);
	m_selectedGraphGroup->addButton(m_w3Btn);
	m_selectedGraphGroup->addButton(m_o1Btn);
	m_selectedGraphGroup->addButton(m_o2Btn);

	Xpressive *e = castModel<Xpressive>();
	m_selectedGraphGroup->setModel(&e->selectedGraph());

	m_sinWaveBtn = new PixmapButton(this, tr("Sine wave"));
	m_sinWaveBtn->move(4, ROW_WAVEBTN);
	m_sinWaveBtn->setActiveGraphic(embed::getIconPixmap("sin_wave_active"));
	m_sinWaveBtn->setInactiveGraphic(embed::getIconPixmap("sin_wave_inactive"));
	ToolTip::add(m_sinWaveBtn, tr("Sine wave"));

	m_moogWaveBtn = new PixmapButton(this, tr("Moog-saw wave"));
	m_moogWaveBtn->move(4, ROW_WAVEBTN-14);
	m_moogWaveBtn->setActiveGraphic(
		embed::getIconPixmap( "moog_saw_wave_active" ) );
	m_moogWaveBtn->setInactiveGraphic(embed::getIconPixmap("moog_saw_wave_inactive"));
	ToolTip::add(m_moogWaveBtn, tr("Moog-saw wave"));

	m_expWaveBtn = new PixmapButton(this, tr("Exponential wave"));
	m_expWaveBtn->move(4 +14, ROW_WAVEBTN-14);
	m_expWaveBtn->setActiveGraphic(embed::getIconPixmap( "exp_wave_active" ) );
	m_expWaveBtn->setInactiveGraphic(embed::getIconPixmap( "exp_wave_inactive" ) );
	ToolTip::add(m_expWaveBtn, tr("Exponential wave"));

	m_sawWaveBtn = new PixmapButton(this, tr("Saw wave"));
	m_sawWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN-14);
	m_sawWaveBtn->setActiveGraphic(embed::getIconPixmap("saw_wave_active"));
	m_sawWaveBtn->setInactiveGraphic(embed::getIconPixmap("saw_wave_inactive"));
	ToolTip::add(m_sawWaveBtn, tr("Saw wave"));

	m_usrWaveBtn = new PixmapButton(this, tr("User-defined wave"));
	m_usrWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN-14);
	m_usrWaveBtn->setActiveGraphic(embed::getIconPixmap("usr_wave_active"));
	m_usrWaveBtn->setInactiveGraphic(embed::getIconPixmap("usr_wave_inactive"));
	ToolTip::add(m_usrWaveBtn, tr("User-defined wave"));

	m_triangleWaveBtn = new PixmapButton(this, tr("Triangle wave"));
	m_triangleWaveBtn->move(4 + 14, ROW_WAVEBTN);
	m_triangleWaveBtn->setActiveGraphic(
			embed::getIconPixmap("triangle_wave_active"));
	m_triangleWaveBtn->setInactiveGraphic(
			embed::getIconPixmap("triangle_wave_inactive"));
	ToolTip::add(m_triangleWaveBtn, tr("Triangle wave"));

	m_sqrWaveBtn = new PixmapButton(this, tr("Square wave"));
	m_sqrWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN);
	m_sqrWaveBtn->setActiveGraphic(embed::getIconPixmap("square_wave_active"));
	m_sqrWaveBtn->setInactiveGraphic(
			embed::getIconPixmap("square_wave_inactive"));
	ToolTip::add(m_sqrWaveBtn, tr("Square wave"));

	m_whiteNoiseWaveBtn = new PixmapButton(this, tr("White noise"));
	m_whiteNoiseWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN);
	m_whiteNoiseWaveBtn->setActiveGraphic(
			embed::getIconPixmap("white_noise_wave_active"));
	m_whiteNoiseWaveBtn->setInactiveGraphic(
			embed::getIconPixmap("white_noise_wave_inactive"));
	ToolTip::add(m_whiteNoiseWaveBtn, tr("White noise"));

	m_waveInterpolate  = new LedCheckBox("Interpolate", this, tr("WaveInterpolate"),
										 LedCheckBox::Green);
	m_waveInterpolate->move(2, 230);

	m_expressionValidToggle = new LedCheckBox("", this, tr("ExpressionValid"),
			LedCheckBox::Red);
	m_expressionValidToggle->move(168, EXPR_TEXT_Y+EXPR_TEXT_H-2);
	m_expressionValidToggle->setEnabled( false );

	m_expressionEditor = new QPlainTextEdit(this);
	m_expressionEditor->move(3, EXPR_TEXT_Y);
	m_expressionEditor->resize(180, EXPR_TEXT_H);

	m_generalPurposeKnob[0] = new XpressiveKnob(this,"A1");
	m_generalPurposeKnob[0]->setHintText(tr("General purpose 1:"), "");
	m_generalPurposeKnob[0]->move(COL_KNOBS, ROW_KNOBSA1);

	m_generalPurposeKnob[1] = new XpressiveKnob(this,"A2");
	m_generalPurposeKnob[1]->setHintText(tr("General purpose 2:"), "");
	m_generalPurposeKnob[1]->move(COL_KNOBS, ROW_KNOBSA2);

	m_generalPurposeKnob[2] = new XpressiveKnob(this,"A3");
	m_generalPurposeKnob[2]->setHintText(tr("General purpose 3:"), "");
	m_generalPurposeKnob[2]->move(COL_KNOBS, ROW_KNOBSA3);

	m_panningKnob[0] = new XpressiveKnob(this,"O1 panning");
	m_panningKnob[0]->setHintText(tr("O1 panning:"), "");
	m_panningKnob[0]->move(COL_KNOBS, ROW_KNOBSP1);

	m_panningKnob[1] = new XpressiveKnob(this,"O2 panning");
	m_panningKnob[1]->setHintText(tr("O2 panning:"), "");
	m_panningKnob[1]->move(COL_KNOBS, ROW_KNOBSP2);

	m_relKnob = new XpressiveKnob(this,"Release transition");
	m_relKnob->setHintText(tr("Release transition:"), "ms");
	m_relKnob->move(COL_KNOBS, ROW_KNOBREL);

	m_smoothKnob=new Knob(knobStyled, this, "Smoothness");
	m_smoothKnob->setFixedSize(25, 25);
	m_smoothKnob->setCenterPointX(12.5);
	m_smoothKnob->setCenterPointY(12.5);
	m_smoothKnob->setInnerRadius(4);
	m_smoothKnob->setOuterRadius(9);
	m_smoothKnob->setTotalAngle(280.0);
	m_smoothKnob->setLineWidth(3);
	m_smoothKnob->setHintText(tr("Smoothness"), "");
	m_smoothKnob->move(66, EXPR_TEXT_Y + EXPR_TEXT_H + 4);

	connect(m_generalPurposeKnob[0], SIGNAL(sliderMoved(float)), this,
			SLOT(expressionChanged()));
	connect(m_generalPurposeKnob[1], SIGNAL(sliderMoved(float)), this,
			SLOT(expressionChanged()));
	connect(m_generalPurposeKnob[2], SIGNAL(sliderMoved(float)), this,
			SLOT(expressionChanged()));

	connect(m_expressionEditor, SIGNAL(textChanged()), this,
			SLOT(expressionChanged()));
	connect(m_smoothKnob, SIGNAL(sliderMoved(float)), this,
			SLOT(smoothChanged()));
	connect(m_graph, SIGNAL(drawn()), this,
			SLOT(graphDrawn()));

	connect(m_sinWaveBtn, SIGNAL(clicked()), this, SLOT(sinWaveClicked()));
	connect(m_triangleWaveBtn, SIGNAL(clicked()), this,
			SLOT(triangleWaveClicked()));
	connect(m_expWaveBtn, SIGNAL(clicked()), this, SLOT(expWaveClicked()));
	connect(m_moogWaveBtn, SIGNAL(clicked()), this,
			SLOT(moogSawWaveClicked()));
	connect(m_sawWaveBtn, SIGNAL(clicked()), this, SLOT(sawWaveClicked()));
	connect(m_sqrWaveBtn, SIGNAL(clicked()), this, SLOT(sqrWaveClicked()));
	connect(m_whiteNoiseWaveBtn, SIGNAL(clicked()), this,
			SLOT(noiseWaveClicked()));
	connect(m_usrWaveBtn, SIGNAL(clicked()), this, SLOT(usrWaveClicked()));
	connect(m_helpBtn, SIGNAL(clicked()), this, SLOT(helpClicked()));

	connect(m_w1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
	connect(m_w2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
	connect(m_w3Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
	connect(m_o1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
	connect(m_o2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));

	updateLayout();
}

XpressiveView::~XpressiveView()
{
}

void XpressiveView::expressionChanged() {
	Xpressive * e = castModel<Xpressive>();
	QByteArray text = m_expressionEditor->toPlainText().toLatin1();

	switch (m_selectedGraphGroup->model()->value()) {
	case W1_EXPR:
		e->wavesExpression(0) = text;
		break;
	case W2_EXPR:
		e->wavesExpression(1) = text;
		break;
	case W3_EXPR:
		e->wavesExpression(2) = text;
		break;
	case O1_EXPR:
		e->outputExpression(0) = text;
		break;
	case O2_EXPR:
		e->outputExpression(1) = text;
		break;
	}
	if (m_wave_expr)
		m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && text.size() == 0);

	if (text.size()>0)
	{
		const unsigned int sample_rate=m_raw_graph->length();
		ExprFront expr(text.constData(),sample_rate);
		float t=0;
		const float f=10,key=5,v=0.5;
		unsigned int i;
		expr.add_variable("t", t);

		if (m_output_expr)
		{
			expr.add_constant("f", f);
			expr.add_constant("key", key);
			expr.add_constant("rel", 0);
			expr.add_constant("trel", 0);
			expr.add_constant("bnote",e->instrumentTrack()->baseNote());
			expr.add_constant("v", v);
			expr.add_constant("tempo", Engine::getSong()->getTempo());
			expr.add_constant("A1", e->parameterA1().value());
			expr.add_constant("A2", e->parameterA2().value());
			expr.add_constant("A3", e->parameterA3().value());
			expr.add_cyclic_vector("W1",e->graphW1().samples(),e->graphW1().length());
			expr.add_cyclic_vector("W2",e->graphW2().samples(),e->graphW2().length());
			expr.add_cyclic_vector("W3",e->graphW3().samples(),e->graphW3().length());
		}
		expr.setIntegrate(&i,sample_rate);
		expr.add_constant("srate",sample_rate);

		const bool parse_ok=expr.compile();

		if (parse_ok) {
			e->exprValid().setValue(0);
			const int length = m_raw_graph->length();
			float * const samples = new float[length];
			for (i = 0; i < length; i++) {
				t = i / (float) length;
				samples[i] = expr.evaluate();
				if (std::isinf(samples[i]) != 0 || std::isnan(samples[i]) != 0)
					samples[i] = 0;
			}
			m_raw_graph->setSamples(samples);
			delete[] samples;
			if (m_wave_expr)
			{
				smoothChanged();
			}
			else
			{
				Engine::getSong()->setModified();
			}
		}
		else
		{
			e->exprValid().setValue(1);
			if (m_output_expr)
				m_raw_graph->clear();
		}
	}
	else
	{
		e->exprValid().setValue(0);
		if (m_output_expr)
			m_raw_graph->clear();
	}
}

void Xpressive::smooth(float smoothness,const graphModel * in,graphModel * out)
{
	out->setSamples(in->samples());
	if (smoothness>0)
	{
		const int guass_size = (int)(smoothness * 5) | 1;
		const int guass_center = guass_size/2;
		const float delta = smoothness;
		const float a= 1.0f / (sqrtf(2.0f * F_PI) * delta);
		float * const guassian = new float [guass_size];
		float sum = 0.0f;
		float temp = 0.0f;
		int i;
		for (i = 0; i < guass_size; i++ )
		{
			temp = (i - guass_center) / delta;
			sum += guassian[i] = a * powf(F_E, -0.5f * temp * temp);
		}
		for (i = 0; i < guass_size; i++ )
		{
			guassian[i] = guassian[i] / sum;
		}
		out->convolve(guassian, guass_size, guass_center);
		delete [] guassian;
	}
}

void XpressiveView::smoothChanged()
{

	Xpressive * e = castModel<Xpressive>();
	float smoothness=0;
	switch (m_selectedGraphGroup->model()->value()) {
	case W1_EXPR:
		smoothness=e->smoothW1().value();
		break;
	case W2_EXPR:
		smoothness=e->smoothW2().value();
		break;
	case W3_EXPR:
		smoothness=e->smoothW3().value();
		break;
	}
	Xpressive::smooth(smoothness,m_raw_graph,m_graph->model());
	switch (m_selectedGraphGroup->model()->value()) {
	case W1_EXPR:
		e->W1().copyFrom(m_graph->model());
		break;
	case W2_EXPR:
		e->W2().copyFrom(m_graph->model());
		break;
	case W3_EXPR:
		e->W3().copyFrom(m_graph->model());
		break;
	}
	Engine::getSong()->setModified();
	m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && m_expressionEditor->toPlainText().size() == 0);
}

void XpressiveView::graphDrawn()
{
	m_raw_graph->setSamples(m_graph->model()->samples());
	Xpressive * e = castModel<Xpressive>();
	switch (m_selectedGraphGroup->model()->value()) {
	case W1_EXPR:
		e->W1().copyFrom(m_graph->model());
		break;
	case W2_EXPR:
		e->W2().copyFrom(m_graph->model());
		break;
	case W3_EXPR:
		e->W3().copyFrom(m_graph->model());
		break;
	}
	Engine::getSong()->setModified();
}

void XpressiveView::modelChanged() {
	Xpressive * b = castModel<Xpressive>();

	m_expressionValidToggle->setModel( &b->exprValid() );
	m_generalPurposeKnob[0]->setModel( &b->parameterA1() );
	m_generalPurposeKnob[1]->setModel( &b->parameterA2() );
	m_generalPurposeKnob[2]->setModel( &b->parameterA3() );

	m_panningKnob[0]->setModel( &b->panning1() );
	m_panningKnob[1]->setModel( &b->panning2() );
	m_relKnob->setModel( &b->relTransition() );
	m_selectedGraphGroup->setModel( &b->selectedGraph() );

	updateLayout();
}

void XpressiveView::updateLayout() {
	Xpressive * e = castModel<Xpressive>();
	m_output_expr=false;
	m_wave_expr=false;
	switch (m_selectedGraphGroup->model()->value()) {
	case W1_EXPR:
		m_wave_expr=true;
		m_graph->setModel(&e->graphW1(), true);
		m_raw_graph=&(e->rawgraphW1());
		m_expressionEditor->setPlainText(e->wavesExpression(0));
		m_smoothKnob->setModel(&e->smoothW1());
		m_graph->setEnabled((e->smoothW1().value() == 0 && e->wavesExpression(0).size() == 0));
		m_waveInterpolate->setModel(&e->interpolateW1());
		m_smoothKnob->show();
		m_usrWaveBtn->show();
		m_waveInterpolate->show();
		break;
	case W2_EXPR:
		m_wave_expr=true;
		m_graph->setModel(&e->graphW2(), true);
		m_raw_graph=&(e->rawgraphW2());
		m_expressionEditor->setPlainText(e->wavesExpression(1));
		m_smoothKnob->setModel(&e->smoothW2());
		m_graph->setEnabled((e->smoothW2().value() == 0 && e->wavesExpression(1).size() == 0));
		m_waveInterpolate->setModel(&e->interpolateW2());
		m_smoothKnob->show();
		m_usrWaveBtn->show();
		m_waveInterpolate->show();
		break;
	case W3_EXPR:
		m_wave_expr=true;
		m_graph->setModel(&e->graphW3(), true);
		m_raw_graph=&(e->rawgraphW3());
		m_expressionEditor->setPlainText(e->wavesExpression(2));
		m_smoothKnob->setModel(&e->smoothW3());
		m_graph->setEnabled((e->smoothW3().value() == 0 && e->wavesExpression(2).size() == 0));
		m_waveInterpolate->setModel(&e->interpolateW3());
		m_smoothKnob->show();
		m_usrWaveBtn->show();
		m_waveInterpolate->show();
		break;
	case O1_EXPR:
		m_output_expr=true;
		m_graph->setModel(&e->graphO1(), true);
		m_raw_graph=&(e->graphO1());
		m_expressionEditor->setPlainText(e->outputExpression(0));
		m_smoothKnob->hide();
		m_graph->setEnabled(false);
		m_usrWaveBtn->hide();
		m_waveInterpolate->hide();
		break;
	case O2_EXPR:
		m_output_expr=true;
		m_graph->setModel(&e->graphO2(), true);
		m_raw_graph=&(e->graphO2());
		m_expressionEditor->setPlainText(e->outputExpression(1));
		m_smoothKnob->hide();
		m_graph->setEnabled(false);
		m_usrWaveBtn->hide();
		m_waveInterpolate->hide();
		break;
	}
}

void XpressiveView::sinWaveClicked() {
	if (m_output_expr)
		m_expressionEditor->appendPlainText("sinew(integrate(f))");
	else
		m_expressionEditor->appendPlainText("sinew(t)");
	Engine::getSong()->setModified();
}

void XpressiveView::triangleWaveClicked() {
	if (m_output_expr)
		m_expressionEditor->appendPlainText("trianglew(integrate(f))");
	else
		m_expressionEditor->appendPlainText("trianglew(t)");
	Engine::getSong()->setModified();
}

void XpressiveView::sawWaveClicked() {
	if (m_output_expr)
		m_expressionEditor->appendPlainText("saww(integrate(f))");
	else
		m_expressionEditor->appendPlainText("saww(t)");
	Engine::getSong()->setModified();
}

void XpressiveView::sqrWaveClicked() {
	if (m_output_expr)
		m_expressionEditor->appendPlainText("squarew(integrate(f))");
	else
		m_expressionEditor->appendPlainText("squarew(t)");
	Engine::getSong()->setModified();
}

void XpressiveView::noiseWaveClicked() {
	m_expressionEditor->appendPlainText("randsv(t*srate,0)");
	Engine::getSong()->setModified();
}

void XpressiveView::moogSawWaveClicked()
{
	if (m_output_expr)
		m_expressionEditor->appendPlainText("moogsaww(integrate(f))");
	else
		m_expressionEditor->appendPlainText("moogsaww(t)");
	Engine::getSong()->setModified();
}
void XpressiveView::expWaveClicked()
{
	if (m_output_expr)
		m_expressionEditor->appendPlainText("expw(integrate(f))");
	else
		m_expressionEditor->appendPlainText("expw(t)");
	Engine::getSong()->setModified();
}

void XpressiveView::usrWaveClicked() {
	m_expressionEditor->setPlainText("");
	QString fileName = m_raw_graph->setWaveToUser();
	smoothChanged();
	Engine::getSong()->setModified();
}

QString XpressiveHelpView::s_helpText=
"<b>O1, O2</b> - Two output waves. Panning is controlled by PN1 and PN2.<br>"
"<b>W1, W2, W3</b> - Wave samples evaluated by expression. In these samples, t variable ranges [0,1).<br>"
"These waves can be used as functions inside the output waves (O1, O2). The wave period is 1.<br>"
"<h4>Available variables:</h4><br>"
"<b>t</b> - Time in seconds.<br>"
"<b>f</b> - Note's pitched frequency. Available only in the output expressions.<br>"
"<b>key</b> - Note's keyboard key. 0 denotes C0, 48 denotes C4, 96 denotes C8. Available only in the output expressions.<br>"
"<b>bnote</b> - Base note. By default it is 57 which means A5, unless you change it.<br>"
"<b>srate</b> - Sample rate. In wave expression it returns the wave's number of samples.<br>"
"<b>tempo</b> - Song's Tempo. Available only in the output expressions.<br>"
"<b>v</b> - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.<br>"
"<b>rel</b> - Gives 0.0 while the key is held, and 1.0 after the key release. Available only in the output expressions.<br>"
"<b>trel</b> - Time after release. While the note is held, it gives 0.0. Afterwards, it starts counting seconds.<br>"
"The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob<br>"
"<b>seed</b> - A random value that remains consistent in the lifetime of a single wave. Meant to be used with <b>randsv</b><br>"
"<b>A1, A2, A3</b> - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].<br>"
"<h4>Available functions:</h4><br>"
"<b>W1, W2, W3</b> - As mentioned before. You can reference them only in O1 and O2.<br>"
"<b>cent(x)</b> - Gives pow(2,x/1200), so you can multiply it with the f variable to pitch the frequency.<br>"
"100 cents equals one semitone<br>"
"<b>semitone(x)</b> - Gives pow(2,x/12), so you can multiply it with the f variable to pitch the frequency.<br>"
"<b>last(n)</b> - Gives you the last n'th evaluated sample. In O1 and O2 it keeps a whole second. Thus the argument n must be in the range [1,srate], or else, it will return 0.<br>"
"<b>integrate(x)</b> - Integrates x by delta t (It sums values and divides them by sample rate).<br>"
"If you use notes with automated frequency, you should use:<br>"
"sinew(integrate(f)) instead of sinew(t*f)<br>"
"<b>randv(x)</b> - A random vector. Each cell is reference by an integer index in the range [0,2^31]<br>"
"Each evaluation of an expression results in different random vector.<br>"
"Although, it remains consistent in the lifetime of a single wave.<br>"
"If you want a single random values you can use randv(0),randv(1)... <br>"
"and every reference to randv(a) will give you the same value."
"If you want a random wave you can use randv(t*srate).<br>"
"Each random value is in the range [-1,1).<br>"
"<b>randsv(x,seed)</b> - works exactly like randv(x),<br>"
"except that it lets you to select the seed manualy,<br>"
"if you want to try different random values and make it consistent in each evaluation.<br>"
"<b>sinew(x)</b> - A sine wave with period of 1 (In contrast to real sine wave which have a period of 2*pi).<br>"
"<b>trianglew(x)</b> - A triangle wave with period of 1.<br>"
"<b>squarew(x)</b> - A square wave with period of 1.<br>"
"<b>saww(x)</b> - A saw wave with period of 1.<br>"
"<b>clamp(min_val,x,max_val)</b> - If x is in range of (min_val,max_val) it returns x. Otherwise if it's greater than max_val it returns max_val, else returns min_val.<br>"
"<b>abs, sin, cos, tan, cot, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, sinc, "
"hypot, exp, log, log2, log10, logn, pow, sqrt, min, max, floor, ceil, round, trunc, frac, "
"avg, sgn, mod, etc. are also available.</b><br>"
"<b>Operands + - * / % ^ > < >= <= == != & | are also available.</b><br>"
"<b>Amplitude Modulation</b> - W1(t*f)*(1+W2(t*f))<br>"
"<b>Ring Modulation</b> - W1(t * f)*W2(t * f)<br>"
"<b>Mix Modulation</b> - 0.5*( W1(t * f) + W2(t * f) )<br>"
"<b>Frequency Modulation</b> - [vol1]*W1( integrate( f + srate*[vol2]*W2( integrate(f) ) ) )<br>"
"<b>Phase Modulation</b> - [vol1]*W1( integrate(f) + [vol2]*W2( integrate(f) ) )<br>"
		;

XpressiveHelpView::XpressiveHelpView():QTextEdit(s_helpText)
{
	setWindowTitle ( "Xpressive Help" );
	setTextInteractionFlags ( Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse );
	gui->mainWindow()->addWindowedWidget( this );
	parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
	parentWidget()->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
	parentWidget()->setFixedSize( 300, 500);

	// No maximize button
	Qt::WindowFlags flags = parentWidget()->windowFlags();
	flags &= ~Qt::WindowMaximizeButtonHint;
	parentWidget()->setWindowFlags( flags );
}

void XpressiveView::helpClicked() {
	XpressiveHelpView::getInstance()->show();

}

extern "C" {

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *m, void *) {
	return (new Xpressive(static_cast<InstrumentTrack *>(m)));
}

}


code:
/*
 * Xpressive.h - Instrument which uses a mathematical formula parser
 *
 * Copyright (c) 2016-2017 Orr Dvori
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef XPRESSIVE_H
#define XPRESSIVE_H

#include <QPlainTextEdit>

#include "Graph.h"
#include "Instrument.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "LedCheckbox.h"
#include "PixmapButton.h"

#include "ExprSynth.h"

class oscillator;
class XpressiveView;

const int	W1_EXPR = 0;
const int	W2_EXPR = 1;
const int	W3_EXPR = 2;
const int	O1_EXPR = 3;
const int	O2_EXPR = 4;
const int	NUM_EXPRS = 5;

class ExprFront;
class SubWindow;

class Xpressive : public Instrument
{
	Q_OBJECT
public:
	Xpressive(InstrumentTrack* instrument_track );
	virtual ~Xpressive();

	virtual void playNote(NotePlayHandle* nph,
						sampleFrame* working_buffer );
	virtual void deleteNotePluginData( NotePlayHandle* nph );

	virtual void saveSettings( QDomDocument& _doc,
							QDomElement& _this );
	virtual void loadSettings( const QDomElement& _this );

	virtual QString nodeName() const;

	virtual PluginView* instantiateView( QWidget * parent );

	graphModel& graphO1() { return m_graphO1; }
	graphModel& graphO2() { return m_graphO2; }
	graphModel& graphW1() { return m_graphW1; }
	graphModel& graphW2() { return m_graphW2; }
	graphModel& graphW3() { return m_graphW3; }
	graphModel& rawgraphW1() { return m_rawgraphW1; }
	graphModel& rawgraphW2() { return m_rawgraphW2; }
	graphModel& rawgraphW3() { return m_rawgraphW3; }
	IntModel& selectedGraph() { return m_selectedGraph; }
	QByteArray& wavesExpression(int i) { return m_wavesExpression[i]; }
	QByteArray& outputExpression(int i) { return m_outputExpression[i]; }

	FloatModel& parameterA1() { return m_parameterA1; }
	FloatModel& parameterA2() { return m_parameterA2; }
	FloatModel& parameterA3() { return m_parameterA3; }
	FloatModel& smoothW1() { return m_smoothW1; }
	FloatModel& smoothW2() { return m_smoothW2; }
	FloatModel& smoothW3() { return m_smoothW3; }
	BoolModel& interpolateW1() { return m_interpolateW1; }
	BoolModel& interpolateW2() { return m_interpolateW2; }
	BoolModel& interpolateW3() { return m_interpolateW3; }
	FloatModel& panning1() { return m_panning1; }
	FloatModel& panning2() { return m_panning2; }
	FloatModel& relTransition() { return m_relTransition; }
	WaveSample& W1() { return m_W1; }
	WaveSample& W2() { return m_W2; }
	WaveSample& W3() { return m_W3; }
	BoolModel& exprValid() { return m_exprValid; }
	static void smooth(float smoothness,const graphModel* in,graphModel* out);
protected:

protected slots:

private:
	graphModel  m_graphO1;
	graphModel  m_graphO2;
	graphModel  m_graphW1;
	graphModel  m_graphW2;
	graphModel  m_graphW3;
	graphModel  m_rawgraphW1;
	graphModel  m_rawgraphW2;
	graphModel  m_rawgraphW3;
	IntModel m_selectedGraph;
	QByteArray m_wavesExpression[3];
	QByteArray m_outputExpression[2];
	FloatModel m_parameterA1;
	FloatModel m_parameterA2;
	FloatModel m_parameterA3;
	FloatModel m_smoothW1;
	FloatModel m_smoothW2;
	FloatModel m_smoothW3;
	BoolModel m_interpolateW1;
	BoolModel m_interpolateW2;
	BoolModel m_interpolateW3;
	FloatModel m_panning1;
	FloatModel m_panning2;
	FloatModel m_relTransition;
	float m_A1,m_A2,m_A3;
	WaveSample m_W1, m_W2, m_W3;

	BoolModel m_exprValid;

} ;

class XpressiveView : public InstrumentViewFixedSize
{
	Q_OBJECT
public:
	XpressiveView( Instrument* _instrument,
					QWidget* _parent );

	virtual ~XpressiveView();
protected:

protected slots:
	void updateLayout();

	void sinWaveClicked();
	void triangleWaveClicked();
	void sqrWaveClicked();
	void sawWaveClicked();
	void noiseWaveClicked();
	void moogSawWaveClicked();
	void expWaveClicked();
	void usrWaveClicked();
	void helpClicked();
	void expressionChanged( );
	void smoothChanged( );
	void graphDrawn( );

private:
	virtual void modelChanged();

	Knob *m_generalPurposeKnob[3];
	Knob *m_panningKnob[2];
	Knob *m_relKnob;
	Knob *m_smoothKnob;
	QPlainTextEdit * m_expressionEditor;

	automatableButtonGroup *m_selectedGraphGroup;
	PixmapButton *m_w1Btn;
	PixmapButton *m_w2Btn;
	PixmapButton *m_w3Btn;
	PixmapButton *m_o1Btn;
	PixmapButton *m_o2Btn;
	PixmapButton *m_sinWaveBtn;
	PixmapButton *m_triangleWaveBtn;
	PixmapButton *m_sqrWaveBtn;
	PixmapButton *m_sawWaveBtn;
	PixmapButton *m_whiteNoiseWaveBtn;
	PixmapButton *m_usrWaveBtn;
	PixmapButton *m_moogWaveBtn;
	PixmapButton *m_expWaveBtn;

	static QPixmap *s_artwork;

	Graph *m_graph;
	graphModel *m_raw_graph;
	LedCheckBox *m_expressionValidToggle;
	LedCheckBox *m_waveInterpolate;
	bool m_output_expr;
	bool m_wave_expr;
} ;

class XpressiveHelpView: public QTextEdit
{
	Q_OBJECT
public:
	static XpressiveHelpView* getInstance()
	{
		static XpressiveHelpView instance;
		return &instance;
	}
	static void finalize()
	{
	}

private:
	XpressiveHelpView();
	static QString s_helpText;
};

#endif


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="17in"
   height="11in"
   viewBox="0 0 431.8 279.4"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.3 (2405546, 2018-03-11)"
   sodipodi:docname="Xpressive.svg">
  <defs
     id="defs2" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.61780345"
     inkscape:cx="1006.8716"
     inkscape:cy="872.08024"
     inkscape:document-units="mm"
     inkscape:current-layer="g956"
     showgrid="false"
     showguides="true"
     inkscape:guide-bbox="true"
     inkscape:measure-start="234.5,1092.07"
     inkscape:measure-end="180.984,1024.7"
     inkscape:window-width="1853"
     inkscape:window-height="1025"
     inkscape:window-x="67"
     inkscape:window-y="27"
     inkscape:window-maximized="1"
     units="in" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(0,-17.6)">
    <path
       sodipodi:nodetypes="ccccccccccccccscssccccc"
       inkscape:connector-curvature="0"
       id="path819"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0;stroke:none;stroke-width:0.65183502"
       d="M 85.029931,192.14174 H 72.13582 L 56.369561,164.82578 40.603301,192.14174 H 27.709189 l 22.18276,-38.62122 c -3.259176,-5.78503 -6.681309,-11.65155 -10.2664,-17.59955 -3.544354,-5.94799 -6.212803,-10.61268 -8.005349,-13.99407 -1.751807,-3.3814 -2.74993,-5.2758 -2.994369,-5.68319 -0.203692,-0.44814 -0.203692,-0.89628 0,-1.34442 0.529617,-1.09997 1.833288,-1.58884 3.911012,-1.46662 h 7.333145 l 16.499573,28.8437 16.499574,-28.8437 h 7.638692 c 1.914764,0 3.096215,0.38702 3.544352,1.16108 0.448135,0.73332 0.549987,1.3444 0.305548,1.83329 -0.244441,0.48886 -0.896272,1.64994 -1.955505,3.48325 -1.059232,1.83328 -2.485121,4.27765 -4.277667,7.33313 -1.751808,3.01474 -3.523983,6.04984 -5.31653,9.10532 -1.751807,3.05548 -3.483244,6.04984 -5.194309,8.98311 -1.711066,2.8925 -3.299915,5.62206 -4.766544,8.18867 7.39425,12.87374 14.788506,25.74748 22.182759,38.62122 z" />
    <path
       inkscape:connector-curvature="0"
       id="path821"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 98.901794,135.55432 q 3.972116,-2.87215 8.860876,-2.87215 h 4.27767 q 7.76091,0 12.40524,4.461 4.64432,4.39988 4.64432,12.34413 v 24.4438 q 0,12.77189 -8.61645,16.74402 -2.99437,1.3444 -7.21091,1.3444 h -6.72205 q -3.97212,0 -6.722053,-2.99437 v 29.88257 H 89.246488 l 0.0611,-86.22555 h 9.594197 z m 19.738376,37.58236 v -22.18277 q 0,-4.76654 -1.03886,-6.35537 -1.89439,-2.93327 -6.29429,-2.93327 h -5.25541 q -1.8944,0 -3.42213,0.97775 -2.811043,1.71107 -2.811043,3.54435 v 30.7381 q 0,1.83329 1.588843,3.36102 2.87215,2.68882 4.64433,2.68882 h 5.25541 q 6.11096,0 7.0276,-6.04984 0.30555,-1.77217 0.30555,-3.78879 z" />
    <path
       inkscape:connector-curvature="0"
       id="path823"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 162.92377,141.60416 -6.17207,-0.0611 q -7.6998,0 -10.02196,5.56097 -0.79442,1.8944 -0.79442,4.33878 v 40.63783 h -10.44973 l -0.0611,-54.14304 q -2.68882,0 -2.68882,-2.01661 v -3.54436 h 12.03858 v 5.56097 q 0.85553,-2.44438 3.11658,-4.03324 2.26105,-1.58884 4.82765,-1.58884 h 10.20529 z" />
    <path
       inkscape:connector-curvature="0"
       id="path825"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 208.51148,178.45321 q -0.1833,9.22754 -7.14982,12.58857 -2.32216,1.09996 -5.31653,1.09996 h -14.48296 q -5.43874,0 -9.0442,-3.97211 -3.54436,-3.8499 -3.54436,-9.34977 v -33.3658 q 0,-5.43874 3.17769,-9.22754 3.48325,-4.03323 8.79978,-4.03323 h 15.52183 q 5.43873,0 8.79977,4.33878 3.11658,3.97212 3.11658,9.53308 v 7.94424 q 0,5.37765 -2.87215,9.04422 -3.05549,3.97211 -8.43311,3.97211 h -17.84398 v 11.06083 q 0,4.88876 4.94987,4.88876 h 9.28866 q 2.07772,0 3.2999,-1.16108 1.28331,-1.22219 1.22218,-3.36102 z m -24.32159,-36.78794 q -4.94987,0 -4.94987,5.1332 v 11.18304 H 192.623 q 2.32216,0 3.91101,-1.2833 1.64997,-1.3444 1.64997,-3.8499 v -6.29428 q 0,-2.5666 -1.64997,-3.72768 -1.58885,-1.16108 -3.91101,-1.16108 z" />
    <path
       inkscape:connector-curvature="0"
       id="path827"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 242.67037,145.51517 q 0.1222,-3.42213 -4.21656,-3.42213 h -7.63868 q -5.25542,0 -4.76656,7.14981 0.12219,1.77218 0.12219,3.29991 0,4.58322 4.64434,4.58322 h 10.69417 q 5.49985,0 8.92198,3.97212 3.36103,3.8499 3.36103,9.28865 v 8.18868 q 0,5.62208 -3.60547,9.53309 -3.72766,4.09433 -9.28864,4.09433 h -13.4441 q -5.98874,0 -9.47199,-3.91101 -3.2999,-3.78878 -3.2999,-9.77753 h 10.51084 q -0.12219,2.13885 1.03887,3.05548 1.22218,0.85554 3.17769,0.85554 h 8.86088 q 4.64433,0 4.64433,-4.58322 v -6.11095 q 0,-4.76654 -4.64433,-4.76654 H 228.1874 q -5.56098,0 -9.22756,-3.42213 -3.66656,-3.48325 -3.66656,-9.04421 v -8.92199 q 0,-5.49986 3.29991,-9.28866 3.60547,-4.03322 8.9831,-4.03322 h 13.13856 q 5.62207,0 9.16642,3.84989 3.2999,3.66657 3.2999,9.41087 z" />
    <path
       inkscape:connector-curvature="0"
       id="path829"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 287.16347,145.51517 q 0.12219,-3.42213 -4.21658,-3.42213 h -7.63867 q -5.25544,0 -4.76656,7.14981 0.1222,1.77218 0.1222,3.29991 0,4.58322 4.64434,4.58322 h 10.69416 q 5.49985,0 8.92198,3.97212 3.36104,3.8499 3.36104,9.28865 v 8.18868 q 0,5.62208 -3.60547,9.53309 -3.72768,4.09433 -9.28865,4.09433 h -13.4441 q -5.98873,0 -9.47198,-3.91101 -3.29991,-3.78878 -3.29991,-9.77753 h 10.51082 q -0.12219,2.13885 1.03887,3.05548 1.2222,0.85554 3.1777,0.85554 h 8.86089 q 4.64431,0 4.64431,-4.58322 v -6.11095 q 0,-4.76654 -4.64431,-4.76654 h -10.08309 q -5.56094,0 -9.22752,-3.42213 -3.66657,-3.48325 -3.66657,-9.04421 v -8.92199 q 0,-5.49986 3.29991,-9.28866 3.60548,-4.03322 8.98311,-4.03322 h 13.13855 q 5.62207,0 9.16642,3.84989 3.29991,3.66657 3.29991,9.41087 z" />
    <path
       inkscape:connector-curvature="0"
       id="path831"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 312.24186,114.71597 q 6.72205,0 6.72205,6.84427 0,6.90537 -6.72205,6.90537 -3.29991,0 -5.07211,-1.77218 -1.77215,-1.83328 -1.77215,-5.13319 0,-3.29993 1.77215,-5.0721 1.7722,-1.77217 5.07211,-1.77217 z m 5.13318,77.12023 h -10.51082 v -59.39847 h 10.51082 z" />
    <path
       inkscape:connector-curvature="0"
       id="path833"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 345.66878,178.02544 q 0.61108,-2.5666 10.93861,-45.5266 h 6.23315 q 2.13884,0 3.23882,1.9555 0.36666,0.54999 0.36666,1.2833 0,0.67222 -0.61111,3.29992 -0.54998,2.5666 -1.52772,6.29429 -0.91664,3.72768 -2.13885,8.31089 -1.22218,4.58322 -2.56658,9.34976 -1.28331,4.76654 -2.56661,9.47198 -1.28331,4.64432 -2.38326,8.55533 -2.56662,9.04421 -2.99438,10.75528 h -11.97747 q -0.42778,-1.40552 -1.34442,-4.461 -0.85552,-3.05548 -2.07771,-7.08871 -1.2222,-4.09433 -2.62772,-8.79976 -1.40551,-4.76655 -2.81103,-9.53309 -1.40553,-4.76655 -2.68883,-9.28865 -1.2833,-4.52212 -2.26104,-8.12758 -2.26105,-8.06644 -2.26105,-9.22753 0,-1.16108 0.97775,-1.95551 1.03885,-0.79442 2.26104,-0.79442 h 6.78316 q 6.84427,25.6049 12.03859,45.5266 z" />
    <path
       inkscape:connector-curvature="0"
       id="path835"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#eff3ff;fill-opacity:1;stroke:#001800;stroke-width:0.65183502;stroke-opacity:1"
       d="m 411.48372,178.45321 q -0.18328,9.22754 -7.14981,12.58857 -2.32217,1.09996 -5.31654,1.09996 h -14.48295 q -5.43876,0 -9.04421,-3.97211 -3.54437,-3.8499 -3.54437,-9.34977 v -33.3658 q 0,-5.43874 3.17771,-9.22754 3.48322,-4.03323 8.79976,-4.03323 h 15.52182 q 5.43876,0 8.79979,4.33878 3.11659,3.97212 3.11659,9.53308 v 7.94424 q 0,5.37765 -2.87215,9.04422 -3.0555,3.97211 -8.43312,3.97211 h -17.84399 v 11.06083 q 0,4.88876 4.94987,4.88876 h 9.28864 q 2.07773,0 3.29994,-1.16108 1.28327,-1.22219 1.22218,-3.36102 z m -24.3216,-36.78794 q -4.94987,0 -4.94987,5.1332 v 11.18304 h 13.38299 q 2.32217,0 3.91102,-1.2833 1.64993,-1.3444 1.64993,-3.8499 v -6.29428 q 0,-2.5666 -1.64993,-3.72768 -1.58885,-1.16108 -3.91102,-1.16108 z" />
    <path
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#50626e;fill-opacity:1;stroke:#000000;stroke-width:0.65183496;stroke-opacity:1"
       d="m 31.793889,113.41583 c -1.648687,0.0267 -2.704098,0.52071 -3.167508,1.48318 -0.203699,0.44815 -0.203699,0.89628 0,1.34442 0.244438,0.40739 1.242553,2.30179 2.994367,5.68318 1.792541,3.3814 4.460996,8.04609 8.005349,13.99409 2.997776,4.97358 5.874553,9.88684 8.644447,14.74649 l 6.472519,-11.23524 -14.872528,-25.99957 h -7.333144 c -0.259717,-0.0152 -0.507975,-0.0203 -0.743502,-0.0165 z m 32.622309,42.83525 -6.47379,11.29889 14.193961,24.59149 H 85.030481 C 78.159057,180.178 71.287615,168.21454 64.416198,156.25108 Z"
       id="path819-7"
       inkscape:connector-curvature="0" />
    <g
       id="g956"
       style="stroke:#000000;stroke-opacity:1"
       transform="matrix(2.4636285,0,0,2.4636285,-59.948071,75.031414)">
      <path
         d="m 47.213951,36.448011 -6.399609,11.087694 h -5.233789 l 9.004101,-15.676562 2.629297,-4.564063 6.697266,-11.707812 2.980857,-5.060626 138.949186,-0.0013 c 0.79306,-7e-6 0.30296,4.198952 -0.53489,4.167335 l -135.515802,-4e-6 c 0,0 -1.116851,1.91157 -1.21607,2.110007 -0.09922,0.198437 -0.363802,0.669726 -0.79375,1.413867 -0.429948,0.744141 -1.008724,1.736328 -1.736328,2.976562 -0.711068,1.223698 -1.430404,2.455664 -2.158008,3.695899 -0.711068,1.240234 -1.413867,2.455665 -2.108398,3.64629 -0.694531,1.174088 -1.339453,2.282031 -1.934766,3.323827 z"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#41bbff;fill-opacity:1;stroke:#000000;stroke-width:0.26465029;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round"
         id="path819-5"
         inkscape:connector-curvature="0"
         sodipodi:nodetypes="cccccccsccssccccc" />
      <path
         sodipodi:nodetypes="ccccccccccc"
         id="path819-7-3"
         d="m 28.89259,32.41518 c -0.669211,0.01085 -1.097608,0.211356 -1.285708,0.602031 -0.08268,0.181901 -0.08268,0.363802 0,0.545703 0.09922,0.165365 0.504359,0.93431 1.215429,2.306836 0.727602,1.372526 1.810743,3.26595 3.249415,5.680273 1.216813,2.018806 2.384512,4.013124 3.508827,5.985682 l 2.62723,-4.560446 -6.036839,-10.553361 h -2.976562 c -0.105421,-0.0062 -0.20619,-0.0083 -0.301792,-0.0067 z"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.79999924px;line-height:1.25;font-family:Uroob;-inkscape-font-specification:Uroob;letter-spacing:0px;word-spacing:0px;fill:#009acd;fill-opacity:1;stroke:#000000;stroke-width:0.26458332;stroke-opacity:1;stroke-linejoin:round;stroke-linecap:round"
         inkscape:connector-curvature="0" />
    </g>
  </g>
</svg>


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(audiofileprocessor audio_file_processor.cpp audio_file_processor.h MOCFILES audio_file_processor.h EMBEDDED_RESOURCES *.png)