Skip to content

Commit

Permalink
Add the function randsv to Xpressive (#4089)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gnudles authored and tresf committed Apr 12, 2018
1 parent 9af7821 commit 0c0bfbd
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 47 additions & 15 deletions plugins/Xpressive/ExprSynth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,30 +296,58 @@ inline unsigned int rotateLeft(unsigned int x, const int b)
return x;
}

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

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

inline float operator()(const float& index)
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 = 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);
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);
}

const int data_size=sizeof(random_data)/sizeof(int);
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;
};

Expand All @@ -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()
{
Expand All @@ -369,6 +397,7 @@ class ExprFrontData
RandomVectorFunction m_rand_vec;
IntegrateFunction<float> *m_integ_func;
LastSampleFunction<float> m_last_func;

};


Expand Down Expand Up @@ -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);
Expand All @@ -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(...)
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion plugins/Xpressive/ExprSynth.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <cmath>
#include <cstddef>
#include <limits>
#include "AutomatableModel.h"
#include "Graph.h"
#include "Instrument.h"
Expand All @@ -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; }
Expand All @@ -52,6 +53,9 @@ class ExprFront
private:
ExprFrontData *m_data;
bool m_valid;

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

};

class WaveSample
Expand Down
35 changes: 20 additions & 15 deletions plugins/Xpressive/Xpressive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

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);
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());
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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -776,7 +777,7 @@ void XpressiveView::sqrWaveClicked() {
}

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

Expand Down Expand Up @@ -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>"
Expand All @@ -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>"
Expand Down

0 comments on commit 0c0bfbd

Please sign in to comment.