diff --git a/data/presets/Xpressive/Accordion.xpf b/data/presets/Xpressive/Accordion.xpf index aa095706374..3e4922abb98 100644 --- a/data/presets/Xpressive/Accordion.xpf +++ b/data/presets/Xpressive/Accordion.xpf @@ -1,20 +1,21 @@ - + - + + + - - - - + + + + - - - + + diff --git a/data/presets/Xpressive/Ambition.xpf b/data/presets/Xpressive/Ambition.xpf index 9d90ca516cd..ed631d1f547 100644 --- a/data/presets/Xpressive/Ambition.xpf +++ b/data/presets/Xpressive/Ambition.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Baby Violin.xpf b/data/presets/Xpressive/Baby Violin.xpf index 11f02c4d920..c63ae8a7a83 100644 --- a/data/presets/Xpressive/Baby Violin.xpf +++ b/data/presets/Xpressive/Baby Violin.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Bad Singer.xpf b/data/presets/Xpressive/Bad Singer.xpf deleted file mode 100644 index b303f590bd2..00000000000 --- a/data/presets/Xpressive/Bad Singer.xpf +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/Xpressive/Creature.xpf b/data/presets/Xpressive/Creature.xpf index 10d4431320a..853b4031380 100644 --- a/data/presets/Xpressive/Creature.xpf +++ b/data/presets/Xpressive/Creature.xpf @@ -10,7 +10,7 @@ - + diff --git a/data/presets/Xpressive/Electric Shock.xpf b/data/presets/Xpressive/Electric Shock.xpf index 65e4b35d844..5ad2d7039b4 100644 --- a/data/presets/Xpressive/Electric Shock.xpf +++ b/data/presets/Xpressive/Electric Shock.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Faded Colors - notes test.xpf b/data/presets/Xpressive/Faded Colors - notes test.xpf deleted file mode 100644 index 53b6ba726c0..00000000000 --- a/data/presets/Xpressive/Faded Colors - notes test.xpf +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/Xpressive/Faded Colors.xpf b/data/presets/Xpressive/Faded Colors.xpf index 469a0b51dd0..d7ac89ce733 100644 --- a/data/presets/Xpressive/Faded Colors.xpf +++ b/data/presets/Xpressive/Faded Colors.xpf @@ -1,22 +1,22 @@ - + - + - - - - + + + + - - - + + + diff --git a/data/presets/Xpressive/Fat Flute.xpf b/data/presets/Xpressive/Fat Flute.xpf deleted file mode 100644 index ada0512d281..00000000000 --- a/data/presets/Xpressive/Fat Flute.xpf +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/Xpressive/Frog.xpf b/data/presets/Xpressive/Frog.xpf index 3dc3f6b29c3..0b39242394b 100644 --- a/data/presets/Xpressive/Frog.xpf +++ b/data/presets/Xpressive/Frog.xpf @@ -1,20 +1,21 @@ - + - + + + - - - - + + + + - - - + + diff --git a/data/presets/Xpressive/Horn.xpf b/data/presets/Xpressive/Horn.xpf index 2377f0566d2..eae28c3dd90 100644 --- a/data/presets/Xpressive/Horn.xpf +++ b/data/presets/Xpressive/Horn.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Low Battery.xpf b/data/presets/Xpressive/Kick.xpf similarity index 62% rename from data/presets/Xpressive/Low Battery.xpf rename to data/presets/Xpressive/Kick.xpf index 6b73b9db3ca..597c2d2c714 100644 --- a/data/presets/Xpressive/Low Battery.xpf +++ b/data/presets/Xpressive/Kick.xpf @@ -1,22 +1,39 @@ - + - - + + + - + - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Piano-Gong.xpf b/data/presets/Xpressive/Piano-Gong.xpf index 1be0557021c..645c228097a 100644 --- a/data/presets/Xpressive/Piano-Gong.xpf +++ b/data/presets/Xpressive/Piano-Gong.xpf @@ -1,21 +1,21 @@ - + - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Rubber Bass.xpf b/data/presets/Xpressive/Rubber Bass.xpf index f3a5774d5a1..e1fdef836ed 100644 --- a/data/presets/Xpressive/Rubber Bass.xpf +++ b/data/presets/Xpressive/Rubber Bass.xpf @@ -10,7 +10,7 @@ - + diff --git a/data/presets/Xpressive/Space Echoes.xpf b/data/presets/Xpressive/Space Echoes.xpf index abeb7e20a80..594861ffffe 100644 --- a/data/presets/Xpressive/Space Echoes.xpf +++ b/data/presets/Xpressive/Space Echoes.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Speaker Swapper.xpf b/data/presets/Xpressive/Speaker Swapper.xpf index 9c9e4f4169e..75c80da0447 100644 --- a/data/presets/Xpressive/Speaker Swapper.xpf +++ b/data/presets/Xpressive/Speaker Swapper.xpf @@ -1,21 +1,21 @@ - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Toss.xpf b/data/presets/Xpressive/Toss.xpf index 9b765203c32..b1259ae0a8b 100644 --- a/data/presets/Xpressive/Toss.xpf +++ b/data/presets/Xpressive/Toss.xpf @@ -1,21 +1,21 @@ - + - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/Untuned Bell.xpf b/data/presets/Xpressive/Untuned Bell.xpf index 4a542b2863f..0a8596a34f3 100644 --- a/data/presets/Xpressive/Untuned Bell.xpf +++ b/data/presets/Xpressive/Untuned Bell.xpf @@ -1,22 +1,22 @@ - + - + - + - - - + + + - - + + - \ No newline at end of file + diff --git a/data/presets/Xpressive/Vibrato.xpf b/data/presets/Xpressive/Vibrato.xpf index 23e8162073c..30df21ad2e1 100644 --- a/data/presets/Xpressive/Vibrato.xpf +++ b/data/presets/Xpressive/Vibrato.xpf @@ -1,21 +1,21 @@ - + - + - + - - - + + + - - + + diff --git a/data/presets/Xpressive/X-Distorted.xpf b/data/presets/Xpressive/X-Distorted.xpf deleted file mode 100644 index 61ee8b6bbb7..00000000000 --- a/data/presets/Xpressive/X-Distorted.xpf +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/Xpressive/CMakeLists.txt b/plugins/Xpressive/CMakeLists.txt index 366381e6228..d24150a92c7 100644 --- a/plugins/Xpressive/CMakeLists.txt +++ b/plugins/Xpressive/CMakeLists.txt @@ -20,8 +20,10 @@ BUILD_PLUGIN(xpressive Xpressive.cpp ExprSynth.cpp Xpressive.h + XpressiveView.h exprtk/exprtk.hpp MOCFILES Xpressive.h + MOCFILES XpressiveView.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" ) diff --git a/plugins/Xpressive/ExprSynth.cpp b/plugins/Xpressive/ExprSynth.cpp index 991e0d3e6b2..2bba8bca177 100644 --- a/plugins/Xpressive/ExprSynth.cpp +++ b/plugins/Xpressive/ExprSynth.cpp @@ -149,9 +149,20 @@ struct LastSampleFunction : public exprtk::ifunction if (!std::isnan(x) && !std::isinf(x)) { const int ix=(int)x; - if (ix>=1 && ix<=m_history_size) + const float xfrc = fraction(x); + if (xfrc == 0) { - return m_samples[(ix + m_pivot_last) % m_history_size]; + if (ix>=1 && ix<=m_history_size) + { + return m_samples[(ix + m_pivot_last) % m_history_size]; + } + } + else + { + if (ix>=1 && ix { m_samples[m_pivot_last] = sample; } + else + { + m_samples[m_pivot_last] = 0; + } if (m_pivot_last == 0) { m_pivot_last = m_history_size - 1; @@ -194,6 +209,26 @@ struct WaveValueFunction : public exprtk::ifunction const std::size_t m_size; }; template +struct EnvelopeFunction : public exprtk::ifunction +{ + using exprtk::ifunction::operator(); + + EnvelopeFunction(const T* v, std::size_t s) + : exprtk::ifunction(1), + m_vec(v), + m_size(s) + {} + + inline T operator()(const T& index) + { + if (index< 1 && index>=0) + return m_vec[(int) ( index * m_size )]; + return 0; + } + const T *m_vec; + const std::size_t m_size; +}; +template struct WaveValueFunctionInterpolate : public exprtk::ifunction { using exprtk::ifunction::operator(); @@ -385,6 +420,10 @@ class ExprFrontData { delete cyclic; } + for (int i = 0; i < m_envelopes.size() ; ++i) + { + delete m_envelopes[i]; + } if (m_integ_func) { delete m_integ_func; @@ -396,6 +435,7 @@ class ExprFrontData std::string m_expression_string; std::vector* > m_cyclics; std::vector* > m_cyclics_interp; + std::vector* > m_envelopes; RandomVectorFunction m_rand_vec; IntegrateFunction *m_integ_func; LastSampleFunction m_last_func; @@ -530,8 +570,7 @@ ExprFront::ExprFront(const char * expr, int 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("e", F_E); // use exp function instead... 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); @@ -574,7 +613,7 @@ bool ExprFront::compile() 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_assignment_ops(); sstore.disable_all_control_structures(); parser_t parser(sstore); @@ -651,6 +690,20 @@ bool ExprFront::add_cyclic_vector(const char* name, const float* data, size_t le } return false; } +bool ExprFront::add_envelope(const char* name, const float* data, size_t length) +{ + try + { + EnvelopeFunction *wvf = new EnvelopeFunction(data, length); + m_data->m_envelopes.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; @@ -717,6 +770,9 @@ ExprSynth::ExprSynth(const WaveSample *gW1, const WaveSample *gW2, const WaveSam 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_envelope("N1", m_W1->m_samples,m_W1->m_length); + e->add_envelope("N2", m_W2->m_samples,m_W2->m_length); + e->add_envelope("N3", m_W3->m_samples,m_W3->m_length); e->add_variable("t", m_note_sample_sec); e->add_variable("f", m_frequency); e->add_variable("rel",m_released); @@ -826,4 +882,4 @@ void ExprSynth::renderOutput(fpp_t frames, sampleFrame *buf) } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/Xpressive/ExprSynth.h b/plugins/Xpressive/ExprSynth.h index f338b78fc38..e7c15b8a4c7 100644 --- a/plugins/Xpressive/ExprSynth.h +++ b/plugins/Xpressive/ExprSynth.h @@ -60,6 +60,7 @@ class ExprFront 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); + bool add_envelope(const char* name, const float* data, size_t length); void setIntegrate(const unsigned int* frameCounter, unsigned int sample_rate); ExprFrontData* getData() { return m_data; } private: diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 18ac8c052af..c76dc92d8f3 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -23,6 +23,7 @@ */ #include "Xpressive.h" +#include "XpressiveView.h" #include #include @@ -99,17 +100,66 @@ Xpressive::Xpressive(InstrumentTrack* instrument_track) : 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_panning1( 0.2, -1.0f, 1.0f, 0.01f, this, tr("Panning 1")), + m_panning2(-0.2, -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_exprValid(false, this), + m_previous_frequency(0), + m_nph_of_previous(NULL) { - 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"; + m_outputExpression[0]="sinew(integrate(f*(1+0.05W3(12t))))*(2^(-(1.1+A2)*t)*(0.4+0.1(1+A3)+0.4W2((2.5+2A1)t))^2)"; + m_outputExpression[1]="var glitch :=prev+clamp(0,t/0.2,1)*(f-prev); N3(integrate(A2+1.01))*W3((integrate(glitch)))"; + m_wavesExpression[0]="floor(t*10)/9-0.5"; + m_wavesExpression[1]="floor(t*3)/2-0.12*exp((t*7)%1)"; + m_wavesExpression[2]="0.2((1+floor(t*5))*sin(((t*2)%1)*7))"; + recalculateWaves(); + +} + + +void calculateWaveGraph(QByteArray text, float smoothness, graphModel * raw_graph, graphModel * smooth_graph) +{ + if (text.size()>0) + { + const unsigned int sample_rate=raw_graph->length(); + ExprFront expr(text.constData(),sample_rate); + float t=0; + unsigned int i; + expr.add_variable("t", t); + expr.setIntegrate(&i,sample_rate); + expr.add_constant("srate",sample_rate); + + const bool parse_ok=expr.compile(); + + if (parse_ok) { + const int length = 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; + } + raw_graph->setSamples(samples); + delete[] samples; + Xpressive::smooth(smoothness,raw_graph,smooth_graph); + } + } } +void Xpressive::recalculateWaves() +{ + calculateWaveGraph(wavesExpression(0), smoothW1().value(), &rawgraphW1(), &graphW1()); + calculateWaveGraph(wavesExpression(1), smoothW2().value(), &rawgraphW2(), &graphW2()); + calculateWaveGraph(wavesExpression(2), smoothW3().value(), &rawgraphW3(), &graphW3()); + m_W1.copyFrom(&m_graphW1); + m_W2.copyFrom(&m_graphW2); + m_W3.copyFrom(&m_graphW3); +} + + void Xpressive::saveSettings(QDomDocument & _doc, QDomElement & _this) { @@ -201,19 +251,40 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { m_A2=m_parameterA2.value(); m_A3=m_parameterA3.value(); - if (nph->totalFramesPlayed() == 0 || nph->m_pluginData == nullptr) { + if (/*nph->totalFramesPlayed() == 0 ||*/ nph->m_pluginData == nullptr) { + nph->m_pluginData = (void*)1; - auto exprO1 = new ExprFront(m_outputExpression[0].constData(), - Engine::audioEngine()->processingSampleRate()); // give the "last" function a whole second - auto exprO2 = new ExprFront(m_outputExpression[1].constData(), Engine::audioEngine()->processingSampleRate()); - auto init_expression_step1 = [this, nph](ExprFront* e) { //lambda function to init exprO1 and exprO2 + ExprFront *exprO1 = new ExprFront(m_outputExpression[0].constData(),Engine::audioEngine()->processingSampleRate());//give the "last" function a whole second + ExprFront *exprO2 = new ExprFront(m_outputExpression[1].constData(),Engine::audioEngine()->processingSampleRate()); + float previous_frequency; + if (m_previous_frequency == 0) + { + previous_frequency=nph->frequency(); + m_nph_of_previous = nph; + m_previous_frequency = previous_frequency; + } + else + { + previous_frequency = m_previous_frequency; + m_nph_of_previous = nph; + m_previous_frequency = nph->frequency(); + } + + + auto init_expression_step1 = [this, nph,previous_frequency](ExprFront* e) { //lambda function to init exprO1 and exprO2 //add the constants and the variables to the expression. + #if ENABLE_CUSTOM_KEY_MAPPING //removed because changes in the microtonal tuner. e->add_constant("key", nph->key());//the key that was pressed. e->add_constant("bnote", nph->instrumentTrack()->baseNote()); // the base note + #endif + + + e->add_constant("prev", previous_frequency);//frequency of previous note e->add_constant("srate", Engine::audioEngine()->processingSampleRate());// sample rate of the audio engine 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); @@ -238,6 +309,10 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { } void Xpressive::deleteNotePluginData(NotePlayHandle* nph) { + if (nph == m_nph_of_previous) + { + m_nph_of_previous = NULL; + } delete static_cast(nph->m_pluginData); } @@ -302,6 +377,7 @@ class XpressiveKnob: public Knob { }; + XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : InstrumentViewFixedSize(_instrument, _parent) @@ -390,6 +466,7 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : auto e = castModel(); m_selectedGraphGroup->setModel(&e->selectedGraph()); + m_selectedGraphGroup->model()->setValue(O1_EXPR); m_sinWaveBtn = new PixmapButton(this, tr("Sine wave")); m_sinWaveBtn->move(4, ROW_WAVEBTN); @@ -455,9 +532,14 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : m_expressionValidToggle->move(168, EXPR_TEXT_Y+EXPR_TEXT_H-2); m_expressionValidToggle->setEnabled( false ); - m_expressionEditor = new QPlainTextEdit(this); + m_expressionEditor = new QTextEdit(this); + m_expressionEditor->setAcceptRichText(false); m_expressionEditor->move(3, EXPR_TEXT_Y); m_expressionEditor->resize(180, EXPR_TEXT_H); + m_expressionEditor->setReadOnly(false); +#ifndef QT_NO_SYNTAXHIGHLIGHTER + m_highlighter = new Highlighter(m_expressionEditor->document()); +#endif m_generalPurposeKnob[0] = new XpressiveKnob(this,"A1"); m_generalPurposeKnob[0]->setHintText(tr("General purpose 1:"), ""); @@ -496,6 +578,15 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : m_smoothKnob->setHintText(tr("Smoothness"), ""); m_smoothKnob->move(66, EXPR_TEXT_Y + EXPR_TEXT_H + 4); + m_smoothOverlay=new QWidget(this); + m_smoothOverlay->setGeometry(66,198,67,25); + m_smoothOverlay->setAutoFillBackground(true); + QPalette overlayPalette= m_smoothOverlay->palette(); + overlayPalette.setColor(QPalette::Window, 0x1e2122); + m_smoothOverlay->setPalette(overlayPalette); + + + connect(m_generalPurposeKnob[0], SIGNAL(sliderMoved(float)), this, SLOT(expressionChanged())); connect(m_generalPurposeKnob[1], SIGNAL(sliderMoved(float)), this, @@ -532,6 +623,8 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : updateLayout(); } +XpressiveView::~XpressiveView(){} + void XpressiveView::expressionChanged() { auto e = castModel(); @@ -562,17 +655,20 @@ void XpressiveView::expressionChanged() { 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; + const float f=10,v=0.5; unsigned int i; expr.add_variable("t", t); if (m_output_expr) { expr.add_constant("f", f); + expr.add_constant("prev", f); + #if ENABLE_CUSTOM_KEY_MAPPING //removed because changes in the microtonal tuner. expr.add_constant("key", key); + expr.add_constant("bnote",e->instrumentTrack()->baseNote()); + #endif 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()); @@ -581,6 +677,9 @@ void XpressiveView::expressionChanged() { 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.add_envelope("N1",e->graphW1().samples(),e->graphW1().length()); + expr.add_envelope("N2",e->graphW2().samples(),e->graphW2().length()); + expr.add_envelope("N3",e->graphW3().samples(),e->graphW3().length()); } expr.setIntegrate(&i,sample_rate); expr.add_constant("srate",sample_rate); @@ -623,6 +722,10 @@ void XpressiveView::expressionChanged() { } } + + + + void XpressiveView::smoothChanged() { @@ -700,11 +803,13 @@ void XpressiveView::updateLayout() { m_raw_graph=&(e->rawgraphW1()); m_expressionEditor->setPlainText(e->wavesExpression(0)); m_smoothKnob->setModel(&e->smoothW1()); + m_smoothOverlay->hide(); 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; @@ -712,6 +817,7 @@ void XpressiveView::updateLayout() { m_raw_graph=&(e->rawgraphW2()); m_expressionEditor->setPlainText(e->wavesExpression(1)); m_smoothKnob->setModel(&e->smoothW2()); + m_smoothOverlay->hide(); m_graph->setEnabled((e->smoothW2().value() == 0 && e->wavesExpression(1).size() == 0)); m_waveInterpolate->setModel(&e->interpolateW2()); m_smoothKnob->show(); @@ -724,6 +830,7 @@ void XpressiveView::updateLayout() { m_raw_graph=&(e->rawgraphW3()); m_expressionEditor->setPlainText(e->wavesExpression(2)); m_smoothKnob->setModel(&e->smoothW3()); + m_smoothOverlay->hide(); m_graph->setEnabled((e->smoothW3().value() == 0 && e->wavesExpression(2).size() == 0)); m_waveInterpolate->setModel(&e->interpolateW3()); m_smoothKnob->show(); @@ -736,6 +843,7 @@ void XpressiveView::updateLayout() { m_raw_graph=&(e->graphO1()); m_expressionEditor->setPlainText(e->outputExpression(0)); m_smoothKnob->hide(); + m_smoothOverlay->show(); m_graph->setEnabled(false); m_usrWaveBtn->hide(); m_waveInterpolate->hide(); @@ -746,6 +854,7 @@ void XpressiveView::updateLayout() { m_raw_graph=&(e->graphO2()); m_expressionEditor->setPlainText(e->outputExpression(1)); m_smoothKnob->hide(); + m_smoothOverlay->show(); m_graph->setEnabled(false); m_usrWaveBtn->hide(); m_waveInterpolate->hide(); @@ -755,55 +864,55 @@ void XpressiveView::updateLayout() { void XpressiveView::sinWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("sinew(integrate(f))"); + m_expressionEditor->append("sinew(integrate(f))"); else - m_expressionEditor->appendPlainText("sinew(t)"); + m_expressionEditor->append("sinew(t)"); Engine::getSong()->setModified(); } void XpressiveView::triangleWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("trianglew(integrate(f))"); + m_expressionEditor->append("trianglew(integrate(f))"); else - m_expressionEditor->appendPlainText("trianglew(t)"); + m_expressionEditor->append("trianglew(t)"); Engine::getSong()->setModified(); } void XpressiveView::sawWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("saww(integrate(f))"); + m_expressionEditor->append("saww(integrate(f))"); else - m_expressionEditor->appendPlainText("saww(t)"); + m_expressionEditor->append("saww(t)"); Engine::getSong()->setModified(); } void XpressiveView::sqrWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("squarew(integrate(f))"); + m_expressionEditor->append("squarew(integrate(f))"); else - m_expressionEditor->appendPlainText("squarew(t)"); + m_expressionEditor->append("squarew(t)"); Engine::getSong()->setModified(); } void XpressiveView::noiseWaveClicked() { - m_expressionEditor->appendPlainText("randsv(t*srate,0)"); + m_expressionEditor->append("randsv(t*srate,0)"); Engine::getSong()->setModified(); } void XpressiveView::moogSawWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("moogsaww(integrate(f))"); + m_expressionEditor->append("moogsaww(integrate(f))"); else - m_expressionEditor->appendPlainText("moogsaww(t)"); + m_expressionEditor->append("moogsaww(t)"); Engine::getSong()->setModified(); } void XpressiveView::expWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("expw(integrate(f))"); + m_expressionEditor->append("expw(integrate(f))"); else - m_expressionEditor->appendPlainText("expw(t)"); + m_expressionEditor->append("expw(t)"); Engine::getSong()->setModified(); } @@ -815,24 +924,41 @@ void XpressiveView::usrWaveClicked() { } QString XpressiveHelpView::s_helpText= -"O1, O2 - Two output waves. Panning is controlled by PN1 and PN2.
" -"W1, W2, W3 - Wave samples evaluated by expression. In these samples, t variable ranges [0,1).
" -"These waves can be used as functions inside the output waves (O1, O2). The wave period is 1.
" +"

Main tabs


" +"O1, O2 - Two output expressions. Panning is controlled by PN1 and PN2 respectively.
" +"W1, W2, W3 - Wave samples that may be either drawn, loaded, or evaluated by expression." +"\tThe main axis variable is 't', and it ranges [0,1).
" +"\tIf you prefer drawing by hand, make sure that the expression field is empty, and that smoothness factor set to 0 (zero) while drawing
" +"\tIf you prefer to load a sample, click on the 'PLUS' icon below the expression field.
" +"\tThese wave samples can be used in two ways in the output expression (O1, O2).
" +"\tThe first way is to use them as cyclic wave functions, where the wave period is [0,1).
" +"\tThe second way is to use them as non-cyclic functions, that are valid in the range [0,1), but returns zero if the input is out of bound.
" +"\tThis can be used for defining static envelopes.
" "

Available variables:


" "t - Time in seconds.
" "f - Note's pitched frequency. Available only in the output expressions.
" +#if ENABLE_CUSTOM_KEY_MAPPING "key - Note's keyboard key. 0 denotes C-1, 60 denotes C4, 127 denotes G9. Available only in the output expressions.
" "bnote - Base note. By default it is 69 which means A4, unless you change it.
" +#endif +"key - Note's keyboard key. 0 denotes C1, 60 denotes C4, 127 denotes G9. Available only in the output expressions.
" +"bnote - Base note. By default it is 69 which means A4, unless you change it.
" "srate - Sample rate. In wave expression it returns the wave's number of samples.
" "tempo - Song's Tempo. Available only in the output expressions.
" +"prev - Frequency of the previous note. Can be used for creating portamento effect (Note slide/glitch).
" +"The classical use case is using \"prev+clamp(0,t/TIME,1)*(f-prev)\" as your frequency (TIME is the number of seconds of the glitch).
" "v - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.
" "rel - Gives 0.0 while the key is held, and 1.0 after the key release. Available only in the output expressions.
" -"trel - Time after release. While the note is held, it gives 0.0. Afterwards, it starts counting seconds.
" "The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob
" +"trel - Time after release. While the note is held, it gives 0.0. Afterwards, it starts counting seconds.
" "seed - A random value that remains consistent in the lifetime of a single wave. Meant to be used with randsv
" "A1, A2, A3 - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].
" +"

Defining variables


" +"The syntax for declaring new variables is
" +"var var_name := initial_value;
" "

Available functions:


" -"W1, W2, W3 - As mentioned before. You can reference them only in O1 and O2.
" +"W1, W2, W3 - As mentioned above, the cyclic wave functions. You can reference them only in O1 and O2.
" +"N1, N2, N3 - As mentioned above, the non-cyclic 'envelope' functions. You can reference them only in O1 and O2.
" "cent(x) - Gives pow(2,x/1200), so you can multiply it with the f variable to pitch the frequency.
" "100 cents equals one semitone
" "semitone(x) - Gives pow(2,x/12), so you can multiply it with the f variable to pitch the frequency.
" @@ -841,12 +967,17 @@ QString XpressiveHelpView::s_helpText= "If you use notes with automated frequency, you should use:
" "sinew(integrate(f)) instead of sinew(t*f)
" "randv(x) - A random vector. Each cell is reference by an integer index in the range [0,2^31]
" -"Each evaluation of an expression results in different random vector.
" -"Although, it remains consistent in the lifetime of a single wave.
" +"Each note playback of an expression results in different random vector.
" +"Although, it remains consistent in the lifetime of a single note playback.
" "If you want a single random values you can use randv(0),randv(1)...
" -"and every reference to randv(a) will give you the same value." -"If you want a random wave you can use randv(t*srate).
" +"and every reference to randv(a) will give you the same value (for the current note playback).
" +"To make it more clear, if you use the randv function in your O1 or O2 formula, each time you activate the formula for a single note, " +"a different random vector will be generated. So if you play a full song that uses Xpressive synth, and use the randv " +"function in O1 or O2 output formula, each playback of the song will never be the same.
" +"But as the formula used to generate each time frame (sample) of the wave for the current note, " +"the randv vector remains the same for each sample generated." "Each random value is in the range [-1,1).
" +"If you want a random wave you can use randv(t*srate).
" "randsv(x,seed) - works exactly like randv(x),
" "except that it lets you to select the seed manualy,
" "if you want to try different random values and make it consistent in each evaluation.
" @@ -858,12 +989,13 @@ QString XpressiveHelpView::s_helpText= "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.
" -"Operands + - * / % ^ > < >= <= == != & | are also available.
" -"Amplitude Modulation - W1(t*f)*(1+W2(t*f))
" -"Ring Modulation - W1(t * f)*W2(t * f)
" -"Mix Modulation - 0.5*( W1(t * f) + W2(t * f) )
" +"Operands + - * / % ^ > < >= <= == != & | AND OR XOR NAND are also available.
" +"Ternary operator is also available: (cond?result_if_true:result_if_false)
" +"Amplitude Modulation - W1( integrate(f) )*( 1 + W2( integrate(f) ) )
" +"Ring Modulation - W1( integrate(f) )*W2( integrate(f) )
" +"Mix Modulation - 0.5*( W1( integrate(f) )+W2( integrate(f) ) )
" "Frequency Modulation - [vol1]*W1( integrate( f + srate*[vol2]*W2( integrate(f) ) ) )
" -"Phase Modulation - [vol1]*W1( integrate(f) + [vol2]*W2( integrate(f) ) )
" +"Phase Modulation - [vol1]*W1( integrate(f)+[vol2]*W2( integrate(f) ) )
" ; XpressiveHelpView::XpressiveHelpView():QTextEdit(s_helpText) @@ -899,13 +1031,113 @@ void XpressiveView::helpClicked() { } +#ifndef QT_NO_SYNTAXHIGHLIGHTER +Highlighter::Highlighter(QTextDocument *parent) + : QSyntaxHighlighter(parent) +{ + HighlightingRule rule; + + keywordClass1Format.setForeground(Qt::darkBlue); + keywordClass1Format.setFontWeight(QFont::Bold); + rule.pattern = QRegularExpression(QStringLiteral("(?|\\:|\\?|\\-")); + rule.format = keywordClass4Format; + highlightingRules.append(rule); + + + numericFormat.setForeground(Qt::green); + rule.pattern = QRegularExpression(QStringLiteral("(?(m))); +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { + return ( new Xpressive( static_cast( m ) ) ); } } diff --git a/plugins/Xpressive/Xpressive.h b/plugins/Xpressive/Xpressive.h index b91957ac480..ed53be6c0b5 100644 --- a/plugins/Xpressive/Xpressive.h +++ b/plugins/Xpressive/Xpressive.h @@ -27,11 +27,13 @@ #define XPRESSIVE_H + #include #include "Graph.h" #include "Instrument.h" -#include "InstrumentView.h" + + #include "ExprSynth.h" @@ -53,8 +55,10 @@ const int NUM_EXPRS = 5; class ExprFront; + namespace gui { + class SubWindow; class XpressiveView; } @@ -82,15 +86,17 @@ class Xpressive : public Instrument 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; } + graphModel& graphW1() { return m_graphW1; } //after smoothing + graphModel& graphW2() { return m_graphW2; } //after smoothing + graphModel& graphW3() { return m_graphW3; } //after smoothing + graphModel& rawgraphW1() { return m_rawgraphW1; } //before smoothing + graphModel& rawgraphW2() { return m_rawgraphW2; } //before smoothing + graphModel& rawgraphW3() { return m_rawgraphW3; } //before smoothing IntModel& selectedGraph() { return m_selectedGraph; } QByteArray& wavesExpression(int i) { return m_wavesExpression[i]; } QByteArray& outputExpression(int i) { return m_outputExpression[i]; } + void recalculateWaves(); + FloatModel& parameterA1() { return m_parameterA1; } FloatModel& parameterA2() { return m_parameterA2; } @@ -142,93 +148,14 @@ protected slots: WaveSample m_W1, m_W2, m_W3; BoolModel m_exprValid; - -} ; - -namespace gui -{ - - -class XpressiveView : public InstrumentViewFixedSize -{ - Q_OBJECT -public: - XpressiveView( Instrument* _instrument, - QWidget* _parent ); - -protected: + float m_previous_frequency; + NotePlayHandle* m_nph_of_previous; -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: - void modelChanged() override; - - 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; -}; - -} // namespace gui } // namespace lmms diff --git a/plugins/Xpressive/XpressiveView.h b/plugins/Xpressive/XpressiveView.h new file mode 100644 index 00000000000..56a602be6ae --- /dev/null +++ b/plugins/Xpressive/XpressiveView.h @@ -0,0 +1,142 @@ +#ifndef XPRESSIVEVIEW_H +#define XPRESSIVEVIEW_H + +#include +#include +#include + +#include "Graph.h" +#include "Instrument.h" +#include "InstrumentView.h" +#include "Knob.h" +#include "LedCheckBox.h" +#include "PixmapButton.h" + + +namespace lmms +{ + +namespace gui +{ + + +#ifndef QT_NO_SYNTAXHIGHLIGHTER + +class Highlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + Highlighter(QTextDocument *parent = 0); + +protected: + void highlightBlock(const QString &text) override; + +private: + struct HighlightingRule + { + QRegularExpression pattern; + QTextCharFormat format; + }; + QVector highlightingRules; + + + QTextCharFormat keywordClass1Format; + QTextCharFormat keywordClass2Format; + QTextCharFormat keywordClass3Format; + QTextCharFormat keywordClass4Format; + QTextCharFormat numericFormat; + QTextCharFormat badFormat; + + +}; +#endif + +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; + QTextEdit * 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; + QWidget *m_smoothOverlay; +#ifndef QT_NO_SYNTAXHIGHLIGHTER + Highlighter *m_highlighter; +#endif + + 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; +}; + +} // namespace gui +} // namespace lmms + + +#endif // XPRESSIVEVIEW_H diff --git a/plugins/Xpressive/exprtk b/plugins/Xpressive/exprtk index 93a9f44f99b..772884eb09e 160000 --- a/plugins/Xpressive/exprtk +++ b/plugins/Xpressive/exprtk @@ -1 +1 @@ -Subproject commit 93a9f44f99b910bfe07cd1e933371e83cea3841c +Subproject commit 772884eb09eb648808a5151a077faf97fa127426 diff --git a/src/gui/instrument/PianoView.cpp b/src/gui/instrument/PianoView.cpp index a2df50e4702..4574fdf64c2 100644 --- a/src/gui/instrument/PianoView.cpp +++ b/src/gui/instrument/PianoView.cpp @@ -694,7 +694,9 @@ void PianoView::focusOutEvent( QFocusEvent * ) !(parentWidget()->parentWidget()-> focusWidget()->inherits( "QLineEdit" ) || parentWidget()->parentWidget()-> - focusWidget()->inherits( "QPlainTextEdit" ) )) + focusWidget()->inherits( "QPlainTextEdit" )|| + parentWidget()->parentWidget()-> + focusWidget()->inherits( "QTextEdit" ) )) { // then reclaim keyboard focus! setFocus();