Files
lmms/plugins/Xpressive/ExprSynth.cpp
Fawn e328a136fc Use C++20 std::numbers, std::lerp() (#7696)
* use c++20 concepts and numbers for lmms_constants.h

* replace lmms::numbers::sqrt2 with std::numbers::sqrt2

* replace lmms::numbers::e with std::numbers::e
Also replace the only use of lmms::numbers::inv_e with a local constexpr instead

* remove lmms::numbers::pi_half and lmms::numbers::pi_sqr
They were only used in one or two places each

* replace lmms::numbers::pi with std::numbers::pi

* add #include <numbers> to every file touched so far
This is probably not needed for some of these files. I'll remove those later

* Remove lmms::numbers

Rest in peace lmms::numbers::tau, my beloved

* Add missing #include <numbers>

* replace stray use of F_EPSILON with approximatelyEqual()

* make many constants inline constexpr
A lot of the remaining constants in lmms_constants.h are specific to
SaProcessor. If they are only used there, shouldn't they be in SaProcessor.h?

* ok then, it's allowed to be signed

* remove #include "lmms_constants.h" for files that don't need it
- And also move F_EPSILON into lmms_math.h
- And also add an overload for fast_rand() to specify a higher and lower bound
- And a bunch of other nonsense

* ok then, it's allowed to be inferred

* ok then, it can accept an integral

* fix typo

* appease msvc

* appease msvc again

* Replace linearInterpolate with std::lerp()

As well as time travel to undo several foolish decisions and squash tiny
commits together

* Fix msvc constexpr warnings

* Fix msvc float to double truncation warning

* Apply two suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* fix silly mistake

* Remove SlicerT's dependence on lmms_math.h

* Allow more type inference on fastRand() and fastPow10f()

* Apply suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Clean up fastRand() a little bit more

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-13 13:15:08 -05:00

834 lines
21 KiB
C++

/*
* 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 <cmath>
#include <random>
#include <numbers>
#include "lmms_math.h"
#include "NotePlayHandle.h"
#include "SampleFrame.h"
#include <exprtk.hpp>
#define WARN_EXPRTK qWarning("ExprTk exception")
namespace lmms
{
using symbol_table_t = exprtk::symbol_table<float>;
using expression_t = exprtk::expression<float>;
using parser_t = exprtk::parser<float>;
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()() override
{ 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) override
{ return Functor::process(x); }
};
template <typename T>
struct IntegrateFunction : public exprtk::ifunction<T>
{
using exprtk::ifunction<T>::operator();
~IntegrateFunction() override
{
delete [] m_counters;
}
IntegrateFunction(const unsigned int* frame, unsigned int sample_rate,unsigned int max_counters) :
exprtk::ifunction<T>(1),
m_firstValue(0),
m_frame(frame),
m_sampleRate(sample_rate),
m_maxCounters(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) override
{
if (m_frame)
{
if (m_nCountersCalls == 0)
{
m_firstValue = *m_frame;
}
if (m_firstValue == *m_frame)
{
++m_nCountersCalls;
if (m_nCountersCalls > m_maxCounters)
{
return 0;
}
m_cc = m_nCounters;
++m_nCounters;
}
else // we moved to the next frame
{
m_frame = 0; // this will indicate that we are no longer in init phase.
}
}
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_sampleRate;
}
unsigned int m_firstValue;
const unsigned int* m_frame;
const unsigned int m_sampleRate;
// number of counters allocated
const unsigned int m_maxCounters;
// number of integrate instances that has counters allocated
unsigned int m_nCounters;
// real number of integrate instances
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();
~LastSampleFunction() override
{
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) override
{
if (!std::isnan(x) && x >= 1 && x <= m_history_size) {
return m_samples[(static_cast<std::size_t>(x) + 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) override
{
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) override
{
const T x = positiveFraction(index) * m_size;
const int ix = (int)x;
const float xfrc = fraction(x);
return std::lerp(m_vec[ix], m_vec[(ix + 1) % m_size], xfrc);
}
const T *m_vec;
const std::size_t m_size;
};
static const auto random_data = std::array<unsigned int, 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 auto 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) override
{
const int irseed = seed < 0 || std::isnan(seed) || std::isinf(seed) ? 0 : static_cast<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) override
{
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);
}
};
} // namespace SimpleRandom
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(nullptr),
m_last_func(last_func_samples)
{}
~ExprFrontData()
{
for (const auto& cyclic : m_cyclics)
{
delete cyclic;
}
for (const auto& cyclic : m_cyclics_interp)
{
delete cyclic;
}
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 std::sin(x * 2 * std::numbers::pi_v<float>);
}
};
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 std::exp2(x / 1200);
}
};
static freefunc1<float,harmonic_cent,true> harmonic_cent_func;
struct harmonic_semitone
{
static inline float process(float x)
{
return std::exp2(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", std::numbers::e_v<float>);
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)
{
auto 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
{
auto 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 == nullptr)
{
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;
}
}
} // namespace lmms