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:
committed by
Tres Finocchiaro
parent
9af7821eb1
commit
0c0bfbd060
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>"
|
||||
|
||||
Submodule plugins/Xpressive/exprtk updated: aad301a6eb...f32d2b4bbb
Reference in New Issue
Block a user