Add the function randsv to Xpressive (#4089)

Adds the function randsv, which gives you persistent upon note plays and waveforms transit in the gui.
Moves lmms exprtk submodule back to latest upstream
This commit is contained in:
gnudles
2018-04-12 18:05:42 +03:00
committed by Tres Finocchiaro
parent 9af7821eb1
commit 0c0bfbd060
5 changed files with 74 additions and 33 deletions

2
.gitmodules vendored
View File

@@ -18,7 +18,7 @@
url = https://github.com/lmms/veal
[submodule "plugins/Xpressive/exprtk"]
path = plugins/Xpressive/exprtk
url = https://github.com/tresf/exprtk
url = https://github.com/ArashPartow/exprtk
[submodule "plugins/LadspaEffect/swh/ladspa"]
path = plugins/LadspaEffect/swh/ladspa
url = https://github.com/swh/ladspa

View File

@@ -296,6 +296,44 @@ inline unsigned int rotateLeft(unsigned int x, const int 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();
@@ -307,19 +345,9 @@ struct RandomVectorFunction : public exprtk::ifunction<float>
inline float operator()(const float& index)
{
if (index < 0 || std::isnan(index) || std::isinf(index))
{
return 0;
}
const unsigned int xi = (unsigned int)index;
const unsigned int si = m_rseed % data_size;
const unsigned int sa = m_rseed / data_size;
unsigned int res=rotateLeft(random_data[(xi + si) % data_size] ^ random_data[(xi / data_size + sa) % data_size],sa % 31 + 1);
res ^= rotateLeft(random_data[(3 * xi + si) % data_size] ^ random_data[(xi / data_size + 2 * sa) % data_size],xi % 31 + 1);
return static_cast<int>(res) / (float)(1 << 31);
return RandomVectorSeedFunction::randv(index,m_rseed);
}
const int data_size=sizeof(random_data)/sizeof(int);
const unsigned int m_rseed;
};
@@ -340,10 +368,10 @@ static freefunc0<float,SimpleRandom::float_random_with_engine,false> simple_rand
class ExprFrontData
{
public:
ExprFrontData():
ExprFrontData(int last_func_samples):
m_rand_vec(SimpleRandom::generator()),
m_integ_func(NULL),
m_last_func(500)
m_last_func(last_func_samples)
{}
~ExprFrontData()
{
@@ -369,6 +397,7 @@ public:
RandomVectorFunction m_rand_vec;
IntegrateFunction<float> *m_integ_func;
LastSampleFunction<float> m_last_func;
};
@@ -489,17 +518,19 @@ struct harmonic_semitone
static freefunc1<float,harmonic_semitone,true> harmonic_semitone_func;
ExprFront::ExprFront(const char * expr)
ExprFront::ExprFront(const char * expr, int last_func_samples)
{
m_valid = false;
try
{
m_data = new ExprFrontData();
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);
@@ -513,6 +544,7 @@ ExprFront::ExprFront(const char * expr)
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(...)
@@ -742,7 +774,7 @@ void ExprSynth::renderOutput(fpp_t frames, sampleFrame *buf)
}
o1 = o1_rawExpr->value();
o2 = o2_rawExpr->value();
last_func1->setLastSample(o1);
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;

View File

@@ -27,6 +27,7 @@
#include <cmath>
#include <cstddef>
#include <limits>
#include "AutomatableModel.h"
#include "Graph.h"
#include "Instrument.h"
@@ -39,7 +40,7 @@ class ExprFront
{
public:
typedef float (*ff1data_functor)(void*, float);
ExprFront(const char* expr);
ExprFront(const char* expr, int last_func_samples);
~ExprFront();
bool compile();
inline bool isValid() { return m_valid; }
@@ -52,6 +53,9 @@ public:
private:
ExprFrontData *m_data;
bool m_valid;
static const int max_float_integer_mask=(1<<(std::numeric_limits<float>::digits))-1;
};
class WaveSample

View File

@@ -202,23 +202,24 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) {
if (nph->totalFramesPlayed() == 0 || nph->m_pluginData == NULL) {
ExprFront *exprO1 = new ExprFront(m_outputExpression[0].constData());
ExprFront *exprO2 = new ExprFront(m_outputExpression[1].constData());
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) {
e->add_constant("key", nph->key());
e->add_constant("bnote", nph->instrumentTrack()->baseNote());
e->add_constant("srate", Engine::mixer()->processingSampleRate());
e->add_constant("v", nph->getVolume() / 255.0);
e->add_constant("tempo", Engine::getSong()->getTempo());
e->add_variable("A1", m_A1);
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());
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,
@@ -520,11 +521,11 @@ void XpressiveView::expressionChanged() {
if (text.size()>0)
{
ExprFront expr(text.constData());
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;
const unsigned int sample_rate=m_raw_graph->length();
expr.add_variable("t", t);
if (m_output_expr)
@@ -776,7 +777,7 @@ void XpressiveView::sqrWaveClicked() {
}
void XpressiveView::noiseWaveClicked() {
m_expressionEditor->appendPlainText("rand");
m_expressionEditor->appendPlainText("randsv(t*srate,0)");
Engine::getSong()->setModified();
}
@@ -821,13 +822,14 @@ QString XpressiveHelpView::s_helpText=
"<b>rel</b> - Gives 0.0 while the key is holded, and 1.0 after the key release. Available only in the output expressions.<br>"
"<b>trel</b> - Time after release. While the note is holded, it gives 0.0. Afterwards, it start 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. The argument n must be in the range [1,500], or else, it will return 0.<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>"
@@ -838,6 +840,9 @@ QString XpressiveHelpView::s_helpText=
"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>"