diff --git a/cmake/modules/BuildPlugin.cmake b/cmake/modules/BuildPlugin.cmake
index 4f5e159dd..f3bea9e31 100644
--- a/cmake/modules/BuildPlugin.cmake
+++ b/cmake/modules/BuildPlugin.cmake
@@ -9,7 +9,7 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME)
CMAKE_PARSE_ARGUMENTS(PLUGIN "" "" "MOCFILES;EMBEDDED_RESOURCES;UICFILES;LINK" ${ARGN})
SET(PLUGIN_SOURCES ${PLUGIN_UNPARSED_ARGUMENTS})
- INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src/gui)
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/include)
ADD_DEFINITIONS(-DPLUGIN_NAME=${PLUGIN_NAME})
diff --git a/data/presets/X-Pressive/Accordion.xpf b/data/presets/X-Pressive/Accordion.xpf
new file mode 100644
index 000000000..a7a3a3b43
--- /dev/null
+++ b/data/presets/X-Pressive/Accordion.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Ambition.xpf b/data/presets/X-Pressive/Ambition.xpf
new file mode 100644
index 000000000..2d93f7c05
--- /dev/null
+++ b/data/presets/X-Pressive/Ambition.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Baby Violin.xpf b/data/presets/X-Pressive/Baby Violin.xpf
new file mode 100644
index 000000000..2e887d3d2
--- /dev/null
+++ b/data/presets/X-Pressive/Baby Violin.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Bad Singer.xpf b/data/presets/X-Pressive/Bad Singer.xpf
new file mode 100644
index 000000000..ca9cfd5a3
--- /dev/null
+++ b/data/presets/X-Pressive/Bad Singer.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Cloud Bass.xpf b/data/presets/X-Pressive/Cloud Bass.xpf
new file mode 100644
index 000000000..4e444f22a
--- /dev/null
+++ b/data/presets/X-Pressive/Cloud Bass.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Creature.xpf b/data/presets/X-Pressive/Creature.xpf
new file mode 100644
index 000000000..b667a9c7f
--- /dev/null
+++ b/data/presets/X-Pressive/Creature.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Dream.xpf b/data/presets/X-Pressive/Dream.xpf
new file mode 100644
index 000000000..3b5dd7da2
--- /dev/null
+++ b/data/presets/X-Pressive/Dream.xpf
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Electric Shock.xpf b/data/presets/X-Pressive/Electric Shock.xpf
new file mode 100644
index 000000000..7dea6fe4a
--- /dev/null
+++ b/data/presets/X-Pressive/Electric Shock.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Faded Colors.xpf b/data/presets/X-Pressive/Faded Colors.xpf
new file mode 100644
index 000000000..84a37826a
--- /dev/null
+++ b/data/presets/X-Pressive/Faded Colors.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Fat Flute.xpf b/data/presets/X-Pressive/Fat Flute.xpf
new file mode 100644
index 000000000..92242114e
--- /dev/null
+++ b/data/presets/X-Pressive/Fat Flute.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Frog.xpf b/data/presets/X-Pressive/Frog.xpf
new file mode 100644
index 000000000..bf8b2b249
--- /dev/null
+++ b/data/presets/X-Pressive/Frog.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Horn.xpf b/data/presets/X-Pressive/Horn.xpf
new file mode 100644
index 000000000..099480569
--- /dev/null
+++ b/data/presets/X-Pressive/Horn.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Low Battery.xpf b/data/presets/X-Pressive/Low Battery.xpf
new file mode 100644
index 000000000..c0e648ac9
--- /dev/null
+++ b/data/presets/X-Pressive/Low Battery.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Piano-Gong.xpf b/data/presets/X-Pressive/Piano-Gong.xpf
new file mode 100644
index 000000000..241f61a55
--- /dev/null
+++ b/data/presets/X-Pressive/Piano-Gong.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Rubber Bass.xpf b/data/presets/X-Pressive/Rubber Bass.xpf
new file mode 100644
index 000000000..73c3648ba
--- /dev/null
+++ b/data/presets/X-Pressive/Rubber Bass.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Space Echoes.xpf b/data/presets/X-Pressive/Space Echoes.xpf
new file mode 100644
index 000000000..1d4d2b543
--- /dev/null
+++ b/data/presets/X-Pressive/Space Echoes.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Speaker Swapper.xpf b/data/presets/X-Pressive/Speaker Swapper.xpf
new file mode 100644
index 000000000..cf80b9304
--- /dev/null
+++ b/data/presets/X-Pressive/Speaker Swapper.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Toss.xpf b/data/presets/X-Pressive/Toss.xpf
new file mode 100644
index 000000000..27a0b3f96
--- /dev/null
+++ b/data/presets/X-Pressive/Toss.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Untuned Bell.xpf b/data/presets/X-Pressive/Untuned Bell.xpf
new file mode 100644
index 000000000..744927063
--- /dev/null
+++ b/data/presets/X-Pressive/Untuned Bell.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/Vibrato.xpf b/data/presets/X-Pressive/Vibrato.xpf
new file mode 100644
index 000000000..34795de11
--- /dev/null
+++ b/data/presets/X-Pressive/Vibrato.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/presets/X-Pressive/X-Distorted.xpf b/data/presets/X-Pressive/X-Distorted.xpf
new file mode 100644
index 000000000..cbe3742a5
--- /dev/null
+++ b/data/presets/X-Pressive/X-Distorted.xpf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/Graph.h b/include/Graph.h
index 38e92a4b4..71e6da60b 100644
--- a/include/Graph.h
+++ b/include/Graph.h
@@ -79,7 +79,8 @@ public:
update();
}
-
+signals:
+ void drawn();
protected:
virtual void paintEvent( QPaintEvent * _pe );
virtual void dropEvent( QDropEvent * _de );
@@ -145,6 +146,8 @@ public:
return( m_samples.data() );
}
+ void convolve(const float *convolution, const int convolutionLength, const int centerOffset);
+
public slots:
void setRange( float _min, float _max );
@@ -165,6 +168,7 @@ public slots:
void normalize();
void invert();
void shiftPhase( int _deg );
+ void clear();
signals:
void lengthChanged();
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 8a464e388..e28e586e0 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -80,6 +80,7 @@ IF("${PLUGIN_LIST}" STREQUAL "")
watsyn
waveshaper
vibed
+ xpressive
zynaddsubfx
)
diff --git a/plugins/xpressive/.gitignore b/plugins/xpressive/.gitignore
new file mode 100644
index 000000000..e750281c0
--- /dev/null
+++ b/plugins/xpressive/.gitignore
@@ -0,0 +1,2 @@
+#ignore the patched file
+exprtk.patched.hpp
diff --git a/plugins/xpressive/CMakeLists.txt b/plugins/xpressive/CMakeLists.txt
new file mode 100644
index 000000000..68cb0a02b
--- /dev/null
+++ b/plugins/xpressive/CMakeLists.txt
@@ -0,0 +1,10 @@
+INCLUDE(BuildPlugin)
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_sc_andor -Dexprtk_disable_return_statement -Dexprtk_disable_break_continue -Dexprtk_disable_comments -Dexprtk_disable_string_capabilities -Dexprtk_disable_rtl_io_file -Dexprtk_disable_rtl_vecops ${WERROR_FLAGS} -fexceptions")
+
+IF(WIN32)
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -Dexprtk_disable_enhanced_features")
+ENDIF()
+
+BUILD_PLUGIN(xpressive expressive_plugin.cpp exprsynth.cpp expressive_plugin.h exprtk.hpp MOCFILES expressive_plugin.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")
+
diff --git a/plugins/xpressive/MIT b/plugins/xpressive/MIT
new file mode 100644
index 000000000..7de41d314
--- /dev/null
+++ b/plugins/xpressive/MIT
@@ -0,0 +1,17 @@
+Copyrights for exprtk.hpp
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plugins/xpressive/artwork.png b/plugins/xpressive/artwork.png
new file mode 100644
index 000000000..d5b39acee
Binary files /dev/null and b/plugins/xpressive/artwork.png differ
diff --git a/plugins/xpressive/expressive_plugin.cpp b/plugins/xpressive/expressive_plugin.cpp
new file mode 100644
index 000000000..75d22fc1e
--- /dev/null
+++ b/plugins/xpressive/expressive_plugin.cpp
@@ -0,0 +1,888 @@
+/*
+ * expressive_plugin.cpp - instrument which uses a mathematical formula parser
+ *
+ * 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 "expressive_plugin.h"
+
+#include
+
+#include "Engine.h"
+#include "Graph.h"
+#include "GuiApplication.h"
+#include "InstrumentTrack.h"
+#include "Knob.h"
+#include "LedCheckbox.h"
+#include "MainWindow.h"
+#include "Mixer.h"
+#include "NotePlayHandle.h"
+#include "Oscillator.h"
+#include "PixmapButton.h"
+#include "Song.h"
+#include "SubWindow.h"
+#include "ToolTip.h"
+
+#include "base64.h"
+#include "lmms_constants.h"
+
+#include "embed.h"
+
+#include "exprsynth.h"
+
+extern "C" {
+
+Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY(
+ PLUGIN_NAME), "X-Pressive", QT_TRANSLATE_NOOP("pluginBrowser",
+ "Mathematical expression parser"), "Orr Dvori", 0x0100,
+ Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL };
+
+}
+
+
+
+/*
+ * nice test:
+O1 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t+0.5))+sinew(t*f)*(0.5+0.5sinew(12*A1*t))
+O2 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t))+sinew(t*f)*(0.5+0.5sinew(12*A1*t+0.5))
+*/
+
+
+/***********************************************************************
+ *
+ * class Expressive
+ *
+ * lmms - plugin
+ *
+ ***********************************************************************/
+#define GRAPH_LENGTH 4096
+
+Expressive::Expressive(InstrumentTrack* instrument_track) :
+ Instrument(instrument_track, &xpressive_plugin_descriptor),
+ m_graphO1(-1.0f, 1.0f, 360, this),
+ m_graphO2(-1.0f, 1.0f, 360, this),
+ m_graphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_graphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_graphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_rawgraphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_rawgraphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_rawgraphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
+ m_selectedGraph(0, 0, 6, this, tr("Selected graph")),
+ m_parameterA1(1, -1.0f, 1.0f, 0.01f, this, tr("A1")),
+ m_parameterA2(1, -1.0f, 1.0f, 0.01f, this, tr("A2")),
+ m_parameterA3(1, -1.0f, 1.0f, 0.01f, this, tr("A3")),
+ m_smoothW1(0, 0.0f, 70.0f, 1.0f, this, tr("W1 smoothing")),
+ m_smoothW2(0, 0.0f, 70.0f, 1.0f, this, tr("W2 smoothing")),
+ m_smoothW3(0, 0.0f, 70.0f, 1.0f, this, tr("W3 smoothing")),
+ m_interpolateW1(false, this),
+ m_interpolateW2(false, this),
+ m_interpolateW3(false, this),
+ m_panning1( 1, -1.0f, 1.0f, 0.01f, this, tr("PAN1")),
+ m_panning2(-1, -1.0f, 1.0f, 0.01f, this, tr("PAN2")),
+ 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_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";
+}
+
+Expressive::~Expressive() {
+}
+
+void Expressive::saveSettings(QDomDocument & _doc, QDomElement & _this) {
+
+ // Save plugin version
+ _this.setAttribute("version", "0.1");
+ _this.setAttribute("O1", QString(m_outputExpression[0]));
+ _this.setAttribute("O2", QString(m_outputExpression[1]));
+ _this.setAttribute("W1", QString(m_wavesExpression[0]));
+ // Save sample shape base64-encoded
+ QString sampleString;
+ base64::encode( (const char*)m_rawgraphW1.samples(),
+ m_rawgraphW1.length() * sizeof(float), sampleString );
+ _this.setAttribute( "W1sample", sampleString );
+
+ _this.setAttribute("W2", QString(m_wavesExpression[1]));
+ base64::encode( (const char*)m_rawgraphW2.samples(),
+ m_rawgraphW2.length() * sizeof(float), sampleString );
+ _this.setAttribute( "W2sample", sampleString );
+ _this.setAttribute("W3", QString(m_wavesExpression[2]));
+ base64::encode( (const char*)m_rawgraphW3.samples(),
+ m_rawgraphW3.length() * sizeof(float), sampleString );
+ _this.setAttribute( "W3sample", sampleString );
+ m_smoothW1.saveSettings(_doc,_this,"smoothW1");
+ m_smoothW2.saveSettings(_doc,_this,"smoothW2");
+ m_smoothW3.saveSettings(_doc,_this,"smoothW3");
+ m_interpolateW1.saveSettings(_doc,_this,"interpolateW1");
+ m_interpolateW2.saveSettings(_doc,_this,"interpolateW2");
+ m_interpolateW3.saveSettings(_doc,_this,"interpolateW3");
+ m_parameterA1.saveSettings(_doc,_this,"A1");
+ m_parameterA2.saveSettings(_doc,_this,"A2");
+ m_parameterA3.saveSettings(_doc,_this,"A3");
+ m_panning1.saveSettings(_doc,_this,"PAN1");
+ m_panning2.saveSettings(_doc,_this,"PAN2");
+ m_relTransition.saveSettings(_doc,_this,"RELTRANS");
+
+}
+
+void Expressive::loadSettings(const QDomElement & _this) {
+
+ m_outputExpression[0]=_this.attribute( "O1").toLatin1();
+ m_outputExpression[1]=_this.attribute( "O2").toLatin1();
+ m_wavesExpression[0]=_this.attribute( "W1").toLatin1();
+ m_wavesExpression[1]=_this.attribute( "W2").toLatin1();
+ m_wavesExpression[2]=_this.attribute( "W3").toLatin1();
+
+ m_smoothW1.loadSettings(_this,"smoothW1");
+ m_smoothW2.loadSettings(_this,"smoothW2");
+ m_smoothW3.loadSettings(_this,"smoothW3");
+ m_interpolateW1.loadSettings(_this,"interpolateW1");
+ m_interpolateW2.loadSettings(_this,"interpolateW2");
+ m_interpolateW3.loadSettings(_this,"interpolateW3");
+ m_parameterA1.loadSettings(_this,"A1");
+ m_parameterA2.loadSettings(_this,"A2");
+ m_parameterA3.loadSettings(_this,"A3");
+ m_panning1.loadSettings(_this,"PAN1");
+ m_panning2.loadSettings(_this,"PAN2");
+ m_relTransition.loadSettings(_this,"RELTRANS");
+
+ int size = 0;
+ char * dst = 0;
+ base64::decode( _this.attribute( "W1sample"), &dst, &size );
+
+ m_rawgraphW1.setSamples( (float*) dst );
+ delete[] dst;
+ base64::decode( _this.attribute( "W2sample"), &dst, &size );
+
+ m_rawgraphW2.setSamples( (float*) dst );
+ delete[] dst;
+ base64::decode( _this.attribute( "W3sample"), &dst, &size );
+
+ m_rawgraphW3.setSamples( (float*) dst );
+ delete[] dst;
+
+ smooth(m_smoothW1.value(),&m_rawgraphW1,&m_graphW1);
+ smooth(m_smoothW2.value(),&m_rawgraphW2,&m_graphW2);
+ smooth(m_smoothW3.value(),&m_rawgraphW3,&m_graphW3);
+ m_W1.copyFrom(&m_graphW1);
+ m_W2.copyFrom(&m_graphW2);
+ m_W3.copyFrom(&m_graphW3);
+}
+
+
+QString Expressive::nodeName() const {
+ return (xpressive_plugin_descriptor.name);
+}
+
+void Expressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) {
+ m_A1=m_parameterA1.value();
+ m_A2=m_parameterA2.value();
+ m_A3=m_parameterA3.value();
+
+ 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);
+ 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_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,
+ Engine::mixer()->processingSampleRate(), &m_panning1, &m_panning2, m_relTransition.value());
+ }
+
+
+
+
+ ExprSynth *ps = static_cast(nph->m_pluginData);
+ const fpp_t frames = nph->framesLeftForCurrentPeriod();
+ const f_cnt_t offset = nph->noteOffset();
+
+ ps->renderOutput(frames, working_buffer + offset);
+
+ instrumentTrack()->processAudioBuffer(working_buffer, frames + offset, nph);
+}
+
+void Expressive::deleteNotePluginData(NotePlayHandle* nph) {
+ delete static_cast(nph->m_pluginData);
+}
+
+PluginView * Expressive::instantiateView(QWidget* parent) {
+ return (new expressiveView(this, parent));
+}
+
+class expressiveKnob: public Knob {
+public:
+ void setStyle()
+ {
+ setFixedSize(29, 29);
+ setCenterPointX(14.5);
+ setCenterPointY(14.5);
+ setInnerRadius(4);
+ setOuterRadius(9);
+ setOuterColor(QColor(0x519fff));
+ setTotalAngle(300.0);
+ setLineWidth(3);
+ }
+ expressiveKnob(QWidget * _parent, const QString & _name) :
+ Knob(knobStyled, _parent,_name) {
+ setStyle();
+ }
+ expressiveKnob(QWidget * _parent) :
+ Knob(knobStyled, _parent) {
+ setStyle();
+ }
+
+};
+
+
+expressiveView::expressiveView(Instrument * _instrument, QWidget * _parent) :
+ InstrumentView(_instrument, _parent)
+
+{
+ const int COL_KNOBS = 194;
+ const int ROW_KNOBSA1 = 26;
+ const int ROW_KNOBSA2 = 26 + 32;
+ const int ROW_KNOBSA3 = 26 + 64;
+ const int ROW_KNOBSP1 = 126;
+ const int ROW_KNOBSP2 = 126 + 32;
+ const int ROW_KNOBREL = 126 + 64;
+ const int ROW_WAVEBTN = 234;
+
+ setAutoFillBackground(true);
+ QPalette pal;
+
+ pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
+ setPalette(pal);
+
+ m_graph = new Graph(this, Graph::LinearStyle, 180, 81);
+ m_graph->move(9, 27);
+ m_graph->setAutoFillBackground(true);
+ m_graph->setGraphColor(QColor(255, 255, 255));
+ m_graph->setEnabled(false);
+
+ /*ToolTip::add(m_graph, tr("Draw your own waveform here "
+ "by dragging your mouse on this graph."));*/
+
+ pal = QPalette();
+ pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("wavegraph"));
+ m_graph->setPalette(pal);
+
+ PixmapButton * m_w1Btn;
+ PixmapButton * m_w2Btn;
+ PixmapButton * m_w3Btn;
+ PixmapButton * m_o1Btn;
+ PixmapButton * m_o2Btn;
+ PixmapButton * m_helpBtn;
+
+ m_w1Btn = new PixmapButton(this, NULL);
+ m_w1Btn->move(9, 111);
+ m_w1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w1_active"));
+ m_w1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w1_inactive"));
+ ToolTip::add(m_w1Btn, tr("Select oscillator W1"));
+
+ m_w2Btn = new PixmapButton(this, NULL);
+ m_w2Btn->move(32, 111);
+ m_w2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w2_active"));
+ m_w2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w2_inactive"));
+ ToolTip::add(m_w2Btn, tr("Select oscillator W2"));
+
+ m_w3Btn = new PixmapButton(this, NULL);
+ m_w3Btn->move(55, 111);
+ m_w3Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w3_active"));
+ m_w3Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w3_inactive"));
+ ToolTip::add(m_w3Btn, tr("Select oscillator W3"));
+
+ m_o1Btn = new PixmapButton(this, NULL);
+ m_o1Btn->move(85, 111);
+ m_o1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o1_active"));
+ m_o1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o1_inactive"));
+ ToolTip::add(m_o1Btn, tr("Select OUTPUT 1"));
+
+ m_o2Btn = new PixmapButton(this, NULL);
+ m_o2Btn->move(107, 111);
+ m_o2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o2_active"));
+ m_o2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o2_inactive"));
+ ToolTip::add(m_o2Btn, tr("Select OUTPUT 2"));
+
+ m_helpBtn = new PixmapButton(this, NULL);
+ m_helpBtn->move(139, 111);
+ m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_active"));
+ m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_inactive"));
+ ToolTip::add(m_helpBtn, tr("Open help window"));
+
+ m_selectedGraphGroup = new automatableButtonGroup(this);
+ m_selectedGraphGroup->addButton(m_w1Btn);
+ m_selectedGraphGroup->addButton(m_w2Btn);
+ m_selectedGraphGroup->addButton(m_w3Btn);
+ m_selectedGraphGroup->addButton(m_o1Btn);
+ m_selectedGraphGroup->addButton(m_o2Btn);
+
+ Expressive *e = castModel();
+ m_selectedGraphGroup->setModel(&e->selectedGraph());
+
+ m_sinWaveBtn = new PixmapButton(this, tr("Sine wave"));
+ m_sinWaveBtn->move(10, ROW_WAVEBTN);
+ m_sinWaveBtn->setActiveGraphic(embed::getIconPixmap("sin_wave_active"));
+ m_sinWaveBtn->setInactiveGraphic(embed::getIconPixmap("sin_wave_inactive"));
+ ToolTip::add(m_sinWaveBtn, tr("Click for a sine-wave."));
+
+ m_moogWaveBtn = new PixmapButton(this, tr("Moog-Saw wave"));
+ m_moogWaveBtn->move(10, ROW_WAVEBTN-14);
+ m_moogWaveBtn->setActiveGraphic(
+ embed::getIconPixmap( "moog_saw_wave_active" ) );
+ m_moogWaveBtn->setInactiveGraphic(embed::getIconPixmap("moog_saw_wave_inactive"));
+ ToolTip::add(m_moogWaveBtn, tr("Click for a Moog-Saw-wave."));
+
+ m_expWaveBtn = new PixmapButton(this, tr("Exponential wave"));
+ m_expWaveBtn->move(10 +14, ROW_WAVEBTN-14);
+ m_expWaveBtn->setActiveGraphic(embed::getIconPixmap( "exp_wave_active" ) );
+ m_expWaveBtn->setInactiveGraphic(embed::getIconPixmap( "exp_wave_inactive" ) );
+ ToolTip::add(m_expWaveBtn, tr("Click for an exponential wave."));
+
+ m_sawWaveBtn = new PixmapButton(this, tr("Saw wave"));
+ m_sawWaveBtn->move(10 + 14 * 2, ROW_WAVEBTN-14);
+ m_sawWaveBtn->setActiveGraphic(embed::getIconPixmap("saw_wave_active"));
+ m_sawWaveBtn->setInactiveGraphic(embed::getIconPixmap("saw_wave_inactive"));
+ ToolTip::add(m_sawWaveBtn, tr("Click here for a saw-wave."));
+
+ m_usrWaveBtn = new PixmapButton(this, tr("User defined wave"));
+ m_usrWaveBtn->move(10 + 14 * 3, ROW_WAVEBTN-14);
+ m_usrWaveBtn->setActiveGraphic(embed::getIconPixmap("usr_wave_active"));
+ m_usrWaveBtn->setInactiveGraphic(embed::getIconPixmap("usr_wave_inactive"));
+ ToolTip::add(m_usrWaveBtn, tr("Click here for a user-defined shape."));
+
+ m_triangleWaveBtn = new PixmapButton(this, tr("Triangle wave"));
+ m_triangleWaveBtn->move(10 + 14, ROW_WAVEBTN);
+ m_triangleWaveBtn->setActiveGraphic(
+ embed::getIconPixmap("triangle_wave_active"));
+ m_triangleWaveBtn->setInactiveGraphic(
+ embed::getIconPixmap("triangle_wave_inactive"));
+ ToolTip::add(m_triangleWaveBtn, tr("Click here for a triangle-wave."));
+
+ m_sqrWaveBtn = new PixmapButton(this, tr("Square wave"));
+ m_sqrWaveBtn->move(10 + 14 * 2, ROW_WAVEBTN);
+ m_sqrWaveBtn->setActiveGraphic(embed::getIconPixmap("square_wave_active"));
+ m_sqrWaveBtn->setInactiveGraphic(
+ embed::getIconPixmap("square_wave_inactive"));
+ ToolTip::add(m_sqrWaveBtn, tr("Click here for a square-wave."));
+
+ m_whiteNoiseWaveBtn = new PixmapButton(this, tr("White noise wave"));
+ m_whiteNoiseWaveBtn->move(10 + 14 * 3, ROW_WAVEBTN);
+ m_whiteNoiseWaveBtn->setActiveGraphic(
+ embed::getIconPixmap("white_noise_wave_active"));
+ m_whiteNoiseWaveBtn->setInactiveGraphic(
+ embed::getIconPixmap("white_noise_wave_inactive"));
+ ToolTip::add(m_whiteNoiseWaveBtn, tr("Click here for white-noise."));
+
+
+ m_waveInterpolate = new LedCheckBox("Interpolate", this, tr("WaveInterpolate"),
+ LedCheckBox::Green);
+ m_waveInterpolate->move(120, 230);
+
+ m_expressionValidToggle = new LedCheckBox("", this, tr("ExpressionValid"),
+ LedCheckBox::Red);
+ m_expressionValidToggle->move(174, 216);
+ m_expressionValidToggle->setEnabled( false );
+
+ m_expressionEditor = new QPlainTextEdit(this);
+ m_expressionEditor->move(9, 128);
+ m_expressionEditor->resize(180, 90);
+
+ m_generalPurposeKnob[0] = new expressiveKnob(this,"A1");
+ m_generalPurposeKnob[0]->setHintText(tr("General purpose 1:"), "");
+ m_generalPurposeKnob[0]->move(COL_KNOBS, ROW_KNOBSA1);
+
+ m_generalPurposeKnob[1] = new expressiveKnob(this,"A2");
+ m_generalPurposeKnob[1]->setHintText(tr("General purpose 2:"), "");
+ m_generalPurposeKnob[1]->move(COL_KNOBS, ROW_KNOBSA2);
+
+ m_generalPurposeKnob[2] = new expressiveKnob(this,"A3");
+ m_generalPurposeKnob[2]->setHintText(tr("General purpose 3:"), "");
+ m_generalPurposeKnob[2]->move(COL_KNOBS, ROW_KNOBSA3);
+
+ m_panningKnob[0] = new expressiveKnob(this,"O1 panning");
+ m_panningKnob[0]->setHintText(tr("O1 panning:"), "");
+ m_panningKnob[0]->move(COL_KNOBS, ROW_KNOBSP1);
+
+ m_panningKnob[1] = new expressiveKnob(this,"O2 panning");
+ m_panningKnob[1]->setHintText(tr("O2 panning:"), "");
+ m_panningKnob[1]->move(COL_KNOBS, ROW_KNOBSP2);
+
+ m_relKnob = new expressiveKnob(this,"Release transition");
+ m_relKnob->setHintText(tr("Release transition:"), "ms");
+ m_relKnob->move(COL_KNOBS, ROW_KNOBREL);
+
+
+
+ m_smoothKnob=new Knob(this,"Smoothness");
+ m_smoothKnob->setHintText(tr("Smoothness"), "");
+ m_smoothKnob->move(80, 220);
+
+ connect(m_generalPurposeKnob[0], SIGNAL(sliderMoved(float)), this,
+ SLOT(expressionChanged()));
+ connect(m_generalPurposeKnob[1], SIGNAL(sliderMoved(float)), this,
+ SLOT(expressionChanged()));
+ connect(m_generalPurposeKnob[2], SIGNAL(sliderMoved(float)), this,
+ SLOT(expressionChanged()));
+
+ connect(m_expressionEditor, SIGNAL(textChanged()), this,
+ SLOT(expressionChanged()));
+ connect(m_smoothKnob, SIGNAL(sliderMoved(float)), this,
+ SLOT(smoothChanged()));
+ connect(m_graph, SIGNAL(drawn()), this,
+ SLOT(graphDrawn()));
+
+ connect(m_sinWaveBtn, SIGNAL(clicked()), this, SLOT(sinWaveClicked()));
+ connect(m_triangleWaveBtn, SIGNAL(clicked()), this,
+ SLOT(triangleWaveClicked()));
+ connect(m_expWaveBtn, SIGNAL(clicked()), this, SLOT(expWaveClicked()));
+ connect(m_moogWaveBtn, SIGNAL(clicked()), this,
+ SLOT(moogSawWaveClicked()));
+ connect(m_sawWaveBtn, SIGNAL(clicked()), this, SLOT(sawWaveClicked()));
+ connect(m_sqrWaveBtn, SIGNAL(clicked()), this, SLOT(sqrWaveClicked()));
+ connect(m_whiteNoiseWaveBtn, SIGNAL(clicked()), this,
+ SLOT(noiseWaveClicked()));
+ connect(m_usrWaveBtn, SIGNAL(clicked()), this, SLOT(usrWaveClicked()));
+ connect(m_helpBtn, SIGNAL(clicked()), this, SLOT(helpClicked()));
+
+ connect(m_w1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
+ connect(m_w2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
+ connect(m_w3Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
+ connect(m_o1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
+ connect(m_o2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
+
+ updateLayout();
+}
+
+expressiveView::~expressiveView()
+{
+}
+
+
+void expressiveView::expressionChanged() {
+ Expressive * e = castModel();
+ QByteArray text = m_expressionEditor->toPlainText().toLatin1();
+
+ switch (m_selectedGraphGroup->model()->value()) {
+ case W1_EXPR:
+ e->wavesExpression(0) = text;
+ break;
+ case W2_EXPR:
+ e->wavesExpression(1) = text;
+ break;
+ case W3_EXPR:
+ e->wavesExpression(2) = text;
+ break;
+ case O1_EXPR:
+ e->outputExpression(0) = text;
+ break;
+ case O2_EXPR:
+ e->outputExpression(1) = text;
+ break;
+ }
+ if (m_wave_expr)
+ m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && text.size() == 0);
+
+ if (text.size()>0)
+ {
+ ExprFront expr(text.constData());
+ 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)
+ {
+ expr.add_constant("f", f);
+ expr.add_constant("key", key);
+ 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());
+ expr.add_constant("A2", e->parameterA2().value());
+ expr.add_constant("A3", e->parameterA3().value());
+ 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.setIntegrate(&i,sample_rate);
+ expr.add_constant("srate",sample_rate);
+
+ const bool parse_ok=expr.compile();
+
+ if (parse_ok) {
+ e->exprValid().setValue(0);
+ const int length = m_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;
+ }
+ m_raw_graph->setSamples(samples);
+ delete[] samples;
+ if (m_wave_expr)
+ {
+ smoothChanged();
+ }
+ else
+ {
+ Engine::getSong()->setModified();
+ }
+ }
+ else
+ {
+ e->exprValid().setValue(1);
+ if (m_output_expr)
+ m_raw_graph->clear();
+ }
+ }
+ else
+ {
+ e->exprValid().setValue(0);
+ if (m_output_expr)
+ m_raw_graph->clear();
+ }
+}
+
+void Expressive::smooth(float smoothness,const graphModel * in,graphModel * out)
+{
+ out->setSamples(in->samples());
+ if (smoothness>0)
+ {
+ const int guass_size = (int)(smoothness * 5) | 1;
+ const int guass_center = guass_size/2;
+ const float delta = smoothness;
+ const float a= 1.0f / (sqrtf(2.0f * F_PI) * delta);
+ float * const guassian = new float [guass_size];
+ float sum = 0.0f;
+ float temp = 0.0f;
+ int i;
+ for (i = 0; i < guass_size; i++ )
+ {
+ temp = (i - guass_center) / delta;
+ sum += guassian[i] = a * powf(F_E, -0.5f * temp * temp);
+ }
+ for (i = 0; i < guass_size; i++ )
+ {
+ guassian[i] = guassian[i] / sum;
+ }
+ out->convolve(guassian, guass_size, guass_center);
+ delete [] guassian;
+ }
+}
+
+
+
+void expressiveView::smoothChanged()
+{
+
+ Expressive * e = castModel();
+ float smoothness=0;
+ switch (m_selectedGraphGroup->model()->value()) {
+ case W1_EXPR:
+ smoothness=e->smoothW1().value();
+ break;
+ case W2_EXPR:
+ smoothness=e->smoothW2().value();
+ break;
+ case W3_EXPR:
+ smoothness=e->smoothW3().value();
+ break;
+ }
+ Expressive::smooth(smoothness,m_raw_graph,m_graph->model());
+ switch (m_selectedGraphGroup->model()->value()) {
+ case W1_EXPR:
+ e->W1().copyFrom(m_graph->model());
+ break;
+ case W2_EXPR:
+ e->W2().copyFrom(m_graph->model());
+ break;
+ case W3_EXPR:
+ e->W3().copyFrom(m_graph->model());
+ break;
+ }
+ Engine::getSong()->setModified();
+ m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && m_expressionEditor->toPlainText().size() == 0);
+}
+
+void expressiveView::graphDrawn()
+{
+ m_raw_graph->setSamples(m_graph->model()->samples());
+ Expressive * e = castModel();
+ switch (m_selectedGraphGroup->model()->value()) {
+ case W1_EXPR:
+ e->W1().copyFrom(m_graph->model());
+ break;
+ case W2_EXPR:
+ e->W2().copyFrom(m_graph->model());
+ break;
+ case W3_EXPR:
+ e->W3().copyFrom(m_graph->model());
+ break;
+ }
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::modelChanged() {
+ Expressive * b = castModel();
+
+ m_expressionValidToggle->setModel( &b->exprValid() );
+ m_generalPurposeKnob[0]->setModel( &b->parameterA1() );
+ m_generalPurposeKnob[1]->setModel( &b->parameterA2() );
+ m_generalPurposeKnob[2]->setModel( &b->parameterA3() );
+
+ m_panningKnob[0]->setModel( &b->panning1() );
+ m_panningKnob[1]->setModel( &b->panning2() );
+ m_relKnob->setModel( &b->relTransition() );
+ m_selectedGraphGroup->setModel( &b->selectedGraph() );
+
+ updateLayout();
+}
+
+void expressiveView::updateLayout() {
+ Expressive * e = castModel();
+ m_output_expr=false;
+ m_wave_expr=false;
+ switch (m_selectedGraphGroup->model()->value()) {
+ case W1_EXPR:
+ m_wave_expr=true;
+ m_graph->setModel(&e->graphW1(), true);
+ m_raw_graph=&(e->rawgraphW1());
+ m_expressionEditor->setPlainText(e->wavesExpression(0));
+ m_smoothKnob->setModel(&e->smoothW1());
+ 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;
+ m_graph->setModel(&e->graphW2(), true);
+ m_raw_graph=&(e->rawgraphW2());
+ m_expressionEditor->setPlainText(e->wavesExpression(1));
+ m_smoothKnob->setModel(&e->smoothW2());
+ m_graph->setEnabled((e->smoothW2().value() == 0 && e->wavesExpression(1).size() == 0));
+ m_waveInterpolate->setModel(&e->interpolateW2());
+ m_smoothKnob->show();
+ m_usrWaveBtn->show();
+ m_waveInterpolate->show();
+ break;
+ case W3_EXPR:
+ m_wave_expr=true;
+ m_graph->setModel(&e->graphW3(), true);
+ m_raw_graph=&(e->rawgraphW3());
+ m_expressionEditor->setPlainText(e->wavesExpression(2));
+ m_smoothKnob->setModel(&e->smoothW3());
+ m_graph->setEnabled((e->smoothW3().value() == 0 && e->wavesExpression(2).size() == 0));
+ m_waveInterpolate->setModel(&e->interpolateW3());
+ m_smoothKnob->show();
+ m_usrWaveBtn->show();
+ m_waveInterpolate->show();
+ break;
+ case O1_EXPR:
+ m_output_expr=true;
+ m_graph->setModel(&e->graphO1(), true);
+ m_raw_graph=&(e->graphO1());
+ m_expressionEditor->setPlainText(e->outputExpression(0));
+ m_smoothKnob->hide();
+ m_graph->setEnabled(false);
+ m_usrWaveBtn->hide();
+ m_waveInterpolate->hide();
+ break;
+ case O2_EXPR:
+ m_output_expr=true;
+ m_graph->setModel(&e->graphO2(), true);
+ m_raw_graph=&(e->graphO2());
+ m_expressionEditor->setPlainText(e->outputExpression(1));
+ m_smoothKnob->hide();
+ m_graph->setEnabled(false);
+ m_usrWaveBtn->hide();
+ m_waveInterpolate->hide();
+ break;
+ }
+}
+
+void expressiveView::sinWaveClicked() {
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("sinew(t*f)");
+ else
+ m_expressionEditor->appendPlainText("sinew(t)");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::triangleWaveClicked() {
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("trianglew(t*f)");
+ else
+ m_expressionEditor->appendPlainText("trianglew(t)");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::sawWaveClicked() {
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("saww(t*f)");
+ else
+ m_expressionEditor->appendPlainText("saww(t)");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::sqrWaveClicked() {
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("squarew(t*f)");
+ else
+ m_expressionEditor->appendPlainText("squarew(t)");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::noiseWaveClicked() {
+ m_expressionEditor->appendPlainText("rand");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::moogSawWaveClicked()
+{
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("moogsaww(t*f)");
+ else
+ m_expressionEditor->appendPlainText("moogsaww(t)");
+ Engine::getSong()->setModified();
+}
+void expressiveView::expWaveClicked()
+{
+ if (m_output_expr)
+ m_expressionEditor->appendPlainText("expw(t*f)");
+ else
+ m_expressionEditor->appendPlainText("expw(t)");
+ Engine::getSong()->setModified();
+}
+
+void expressiveView::usrWaveClicked() {
+ m_expressionEditor->setPlainText("");
+ QString fileName = m_raw_graph->setWaveToUser();
+ smoothChanged();
+ Engine::getSong()->setModified();
+}
+
+expressiveHelpView* expressiveHelpView::s_instance=0;
+
+QString expressiveHelpView::s_helpText=
+"O1, O2 - Two output waves. Panning is controled 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.
"
+"Available variables:
"
+"t - Time in seconds.
"
+"f - Note's pitched frequency. Available only in the output expressions.
"
+"key - Note's keyboard key. 0 denotes C0, 48 denotes C4, 96 denotes C8. Available only in the output expressions.
"
+"bnote - Base note. By default it is 57 which means A5, 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.
"
+"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 holded, and 1.0 after the key release. Available only in the output expressions.
"
+"trel - Time after release. While the note is holded, it gives 0.0. Afterwards, it start counting seconds.
"
+"The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob
"
+"A1, A2, A3 - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].
"
+"Available functions:
"
+"W1, W2, W3 - As mentioned before. 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.
"
+"last(n) - Gives you the last n'th evaluated sample. The argument n must be in the range [1,500], or else, it will return 0.
"
+"integrate(x) - Integrates x by delta t (It sums values and divides them by sample rate).
"
+"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.
"
+"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).
"
+"Each random value is in the range [-1,1).
"
+"sinew(x) - A sine wave with period of 1 (In contrast to real sine wave which have a period of 2*pi).
"
+"trianglew(x) - A triangle wave with period of 1.
"
+"squarew(x) - A square wave with period of 1.
"
+"saww(x) - A saw wave with period of 1.
"
+"clamp(min_val,x,max_val) - If x is in range of (min_val,max_val) it returns x. Otherwise if it's greater than max_val it returns max_val, else returns min_val.
"
+"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) )
"
+"Frequency Modulation - [vol1]*W1( integrate( f + srate*[vol2]*W2( integrate(f) ) ) )
"
+"Phase Modulation - [vol1]*W1( integrate(f) + [vol2]*W2( integrate(f) ) )
"
+ ;
+
+expressiveHelpView::expressiveHelpView():QTextEdit(s_helpText)
+{
+ setWindowTitle ( "X-Pressive Help" );
+ setTextInteractionFlags ( Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse );
+ gui->mainWindow()->addWindowedWidget( this );
+ parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
+ parentWidget()->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
+ parentWidget()->setFixedSize( 300, 500);
+}
+
+void expressiveView::helpClicked() {
+ expressiveHelpView::getInstance()->show();
+
+}
+
+__attribute__((destructor)) static void module_destroy()
+{
+ expressiveHelpView::finalize();
+}
+
+extern "C" {
+
+// necessary for getting instance out of shared lib
+Plugin * PLUGIN_EXPORT lmms_plugin_main(Model *, void * _data) {
+ return (new Expressive(static_cast(_data)));
+}
+
+}
+
+
+
+
diff --git a/plugins/xpressive/expressive_plugin.h b/plugins/xpressive/expressive_plugin.h
new file mode 100644
index 000000000..55392bce6
--- /dev/null
+++ b/plugins/xpressive/expressive_plugin.h
@@ -0,0 +1,227 @@
+/*
+ * expressive_plugin.h - instrument which uses a mathematical formula
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef EXPRESSIVE_PLUGIN_H
+#define EXPRESSIVE_PLUGIN_H
+
+#include
+
+#include "Graph.h"
+#include "Instrument.h"
+#include "InstrumentView.h"
+#include "Knob.h"
+#include "LedCheckbox.h"
+#include "PixmapButton.h"
+
+#include "exprsynth.h"
+
+class oscillator;
+class expressiveView;
+
+const int W1_EXPR = 0;
+const int W2_EXPR = 1;
+const int W3_EXPR = 2;
+const int O1_EXPR = 3;
+const int O2_EXPR = 4;
+const int NUM_EXPRS = 5;
+
+
+class ExprFront;
+class SubWindow;
+
+
+
+class Expressive : public Instrument
+{
+ Q_OBJECT
+public:
+ Expressive(InstrumentTrack* instrument_track );
+ virtual ~Expressive();
+
+ virtual void playNote(NotePlayHandle* nph,
+ sampleFrame* working_buffer );
+ virtual void deleteNotePluginData( NotePlayHandle* nph );
+
+
+ virtual void saveSettings( QDomDocument& _doc,
+ QDomElement& _this );
+ virtual void loadSettings( const QDomElement& _this );
+
+ virtual QString nodeName() const;
+
+ virtual PluginView* instantiateView( QWidget * parent );
+
+ 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; }
+ IntModel& selectedGraph() { return m_selectedGraph; }
+ QByteArray& wavesExpression(int i) { return m_wavesExpression[i]; }
+ QByteArray& outputExpression(int i) { return m_outputExpression[i]; }
+
+ FloatModel& parameterA1() { return m_parameterA1; }
+ FloatModel& parameterA2() { return m_parameterA2; }
+ FloatModel& parameterA3() { return m_parameterA3; }
+ FloatModel& smoothW1() { return m_smoothW1; }
+ FloatModel& smoothW2() { return m_smoothW2; }
+ FloatModel& smoothW3() { return m_smoothW3; }
+ BoolModel& interpolateW1() { return m_interpolateW1; }
+ BoolModel& interpolateW2() { return m_interpolateW2; }
+ BoolModel& interpolateW3() { return m_interpolateW3; }
+ FloatModel& panning1() { return m_panning1; }
+ FloatModel& panning2() { return m_panning2; }
+ FloatModel& relTransition() { return m_relTransition; }
+ WaveSample& W1() { return m_W1; }
+ WaveSample& W2() { return m_W2; }
+ WaveSample& W3() { return m_W3; }
+ BoolModel& exprValid() { return m_exprValid; }
+ static void smooth(float smoothness,const graphModel* in,graphModel* out);
+protected:
+
+protected slots:
+
+
+private:
+ graphModel m_graphO1;
+ graphModel m_graphO2;
+ graphModel m_graphW1;
+ graphModel m_graphW2;
+ graphModel m_graphW3;
+ graphModel m_rawgraphW1;
+ graphModel m_rawgraphW2;
+ graphModel m_rawgraphW3;
+ IntModel m_selectedGraph;
+ QByteArray m_wavesExpression[3];
+ QByteArray m_outputExpression[2];
+ FloatModel m_parameterA1;
+ FloatModel m_parameterA2;
+ FloatModel m_parameterA3;
+ FloatModel m_smoothW1;
+ FloatModel m_smoothW2;
+ FloatModel m_smoothW3;
+ BoolModel m_interpolateW1;
+ BoolModel m_interpolateW2;
+ BoolModel m_interpolateW3;
+ FloatModel m_panning1;
+ FloatModel m_panning2;
+ FloatModel m_relTransition;
+ float m_A1,m_A2,m_A3;
+ WaveSample m_W1, m_W2, m_W3;
+
+ BoolModel m_exprValid;
+
+} ;
+
+
+class expressiveView : public InstrumentView
+{
+ Q_OBJECT
+public:
+ expressiveView( Instrument* _instrument,
+ QWidget* _parent );
+
+ virtual ~expressiveView();
+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;
+ 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 expressiveHelpView: public QTextEdit
+{
+ Q_OBJECT
+public:
+ static expressiveHelpView* getInstance()
+ {
+ if (!s_instance)
+ {
+ s_instance = new expressiveHelpView();
+ }
+ return s_instance;
+ }
+ static void finalize()
+ {
+ if (s_instance) { delete s_instance; }
+ }
+
+private:
+ expressiveHelpView();
+ static expressiveHelpView *s_instance;
+ static QString s_helpText;
+
+};
+
+#endif
diff --git a/plugins/xpressive/exprsynth.cpp b/plugins/xpressive/exprsynth.cpp
new file mode 100644
index 000000000..21a517ca5
--- /dev/null
+++ b/plugins/xpressive/exprsynth.cpp
@@ -0,0 +1,792 @@
+/*
+ * exprfront.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
+#include
+#include
+#include
+#include
+
+#include "expressive_plugin.h"
+
+#include "interpolation.h"
+#include "lmms_math.h"
+#include "NotePlayHandle.h"
+
+
+#include "exprtk.hpp"
+
+#define WARN_EXPRTK qWarning("ExprTk exception")
+typedef exprtk::symbol_table symbol_table_t;
+typedef exprtk::expression expression_t;
+typedef exprtk::parser parser_t;
+
+template
+struct freefunc0 : public exprtk::ifunction
+{
+ using exprtk::ifunction::operator();
+
+ freefunc0() : exprtk::ifunction(0) {
+ if (optimize) { exprtk::disable_has_side_effects(*this); }
+ }
+ inline T operator()()
+ { return Functor::process(); }
+};
+template
+struct freefunc1 : public exprtk::ifunction
+{
+ using exprtk::ifunction::operator();
+
+ freefunc1() : exprtk::ifunction(1) {
+ if (optimize) { exprtk::disable_has_side_effects(*this); }
+ }
+ inline T operator()(const T& x)
+ { return Functor::process(x); }
+};
+
+template
+struct IntegrateFunction : public exprtk::ifunction
+{
+
+ using exprtk::ifunction::operator();
+ virtual ~IntegrateFunction()
+ {
+ delete [] m_counters;
+ }
+
+ IntegrateFunction(const unsigned int* frame, unsigned int sample_rate,unsigned int max_counters) :
+ exprtk::ifunction(1),
+ m_frame(frame),
+ m_sample_rate(sample_rate),
+ m_max_counters(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)
+ {
+ if (*m_frame == 0)
+ {
+ ++m_nCountersCalls;
+ if (m_nCountersCalls > m_max_counters)
+ {
+ return 0;
+ }
+ m_cc = m_nCounters;
+ ++m_nCounters;
+ }
+
+ 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_sample_rate;
+ }
+
+ const unsigned int* const m_frame;
+ const unsigned int m_sample_rate;
+ const unsigned int m_max_counters;
+ unsigned int m_nCounters;
+ unsigned int m_nCountersCalls;
+ unsigned int m_cc;
+ double *m_counters;
+};
+
+template
+struct LastSampleFunction : public exprtk::ifunction
+{
+
+ using exprtk::ifunction::operator();
+ virtual ~LastSampleFunction()
+ {
+ delete [] m_samples;
+ }
+
+ LastSampleFunction(unsigned int history_size) :
+ exprtk::ifunction(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)
+ {
+ if (!std::isnan(x) && !std::isinf(x))
+ {
+ const int ix=(int)x;
+ if (ix>=1 && ix<=m_history_size)
+ {
+ return m_samples[(ix + 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
+struct WaveValueFunction : public exprtk::ifunction
+{
+ using exprtk::ifunction::operator();
+
+ WaveValueFunction(const T* v, std::size_t s)
+ : exprtk::ifunction(1),
+ m_vec(v),
+ m_size(s)
+ {}
+
+ inline T operator()(const T& index)
+ {
+ return m_vec[(int) ( positiveFraction(index) * m_size )];
+ }
+ const T *m_vec;
+ const std::size_t m_size;
+};
+template
+struct WaveValueFunctionInterpolate : public exprtk::ifunction
+{
+ using exprtk::ifunction::operator();
+
+ WaveValueFunctionInterpolate(const T* v, std::size_t s)
+ : exprtk::ifunction(1),
+ m_vec(v),
+ m_size(s)
+ {}
+
+ inline T operator()(const T& index)
+ {
+ const T x = positiveFraction(index) * m_size;
+ const int ix = (int)x;
+ const float xfrc = fraction(x);
+ return linearInterpolate(m_vec[ix], m_vec[(ix + 1) % m_size], xfrc);
+ }
+ const T *m_vec;
+ const std::size_t m_size;
+};
+static const unsigned int random_data[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 RandomVectorFunction : public exprtk::ifunction
+{
+ using exprtk::ifunction::operator();
+
+ RandomVectorFunction(const unsigned int seed) :
+ exprtk::ifunction(1),
+ m_rseed(seed)
+ { exprtk::disable_has_side_effects(*this); }
+
+ 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(res) / (float)(1 << 31);
+ }
+
+ const int data_size=sizeof(random_data)/sizeof(int);
+ const unsigned int m_rseed;
+};
+
+namespace SimpleRandom {
+ std::mt19937 generator (17); // mt19937 is a standard mersenne_twister_engine
+ std::uniform_real_distribution dist(-1.0f, 1.0f);
+ struct float_random_with_engine
+ {
+ static inline float process()
+ {
+ return dist(generator);
+ }
+ };
+}
+
+static freefunc0 simple_rand;
+
+class ExprFrontData
+{
+public:
+ ExprFrontData():
+ m_rand_vec(SimpleRandom::generator()),
+ m_integ_func(NULL),
+ m_last_func(500)
+ {}
+ ~ExprFrontData()
+ {
+ for (int i = 0; i < m_cyclics.size() ; ++i)
+ {
+ delete m_cyclics[i];
+ }
+ for (int i = 0; i < m_cyclics_interp.size() ; ++i)
+ {
+ delete m_cyclics_interp[i];
+ }
+ 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* > m_cyclics;
+ std::vector* > m_cyclics_interp;
+ RandomVectorFunction m_rand_vec;
+ IntegrateFunction *m_integ_func;
+ LastSampleFunction m_last_func;
+};
+
+
+struct sin_wave
+{
+ static inline float process(float x)
+ {
+ x = positiveFraction(x);
+ return sinf(x * F_2PI);
+ }
+};
+static freefunc1 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 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 triangle_wave_func;
+struct saw_wave
+{
+ static inline float process(float x)
+ {
+ x=positiveFraction(x);
+ return 2.0f * x - 1.0f;
+ }
+};
+static freefunc1 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 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 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 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 exp2_wave_func;
+struct harmonic_cent
+{
+ static inline float process(float x)
+ {
+ return powf(2, x / 1200);
+ }
+};
+static freefunc1 harmonic_cent_func;
+struct harmonic_semitone
+{
+ static inline float process(float x)
+ {
+ return powf(2, x / 12);
+ }
+};
+static freefunc1 harmonic_semitone_func;
+
+
+ExprFront::ExprFront(const char * expr)
+{
+ m_valid = false;
+ try
+ {
+ m_data = new ExprFrontData();
+
+ 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_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("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)
+ {
+ WaveValueFunctionInterpolate *wvf = new WaveValueFunctionInterpolate(data, length);
+ m_data->m_cyclics_interp.push_back(wvf);
+ return m_data->m_symbol_table.add_function(name, *wvf);
+ }
+ else
+ {
+ WaveValueFunction *wvf = new WaveValueFunction(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 == NULL)
+ {
+ const unsigned int ointeg = find_occurances(m_data->m_expression_string,"integrate");
+ if ( ointeg > 0 )
+ {
+ m_data->m_integ_func = new IntegrateFunction(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 * last_func1 = &m_exprO1->getData()->m_last_func;
+ LastSampleFunction * 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);
+ 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;
+ }
+}
diff --git a/plugins/xpressive/exprsynth.h b/plugins/xpressive/exprsynth.h
new file mode 100644
index 000000000..fe2e92172
--- /dev/null
+++ b/plugins/xpressive/exprsynth.h
@@ -0,0 +1,141 @@
+/*
+ * exprfront.h - header file to 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.
+ *
+ */
+
+#ifndef EXPRFRONT_H_
+#define EXPRFRONT_H_
+
+#include
+#include
+#include "AutomatableModel.h"
+#include "Graph.h"
+#include "Instrument.h"
+#include "MemoryManager.h"
+
+
+class ExprFrontData;
+
+class ExprFront
+{
+public:
+ typedef float (*ff1data_functor)(void*, float);
+ ExprFront(const char* expr);
+ ~ExprFront();
+ bool compile();
+ inline bool isValid() { return m_valid; }
+ float evaluate();
+ 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);
+ void setIntegrate(const unsigned int* frameCounter, unsigned int sample_rate);
+ ExprFrontData* getData() { return m_data; }
+private:
+ ExprFrontData *m_data;
+ bool m_valid;
+};
+
+class WaveSample
+{
+public:
+ WaveSample(int length)
+ {
+ m_length = length;
+ m_samples = new float[m_length];
+ for(int i = 0 ; i < m_length ; ++i)
+ m_samples[i] = 0;
+ }
+ WaveSample(const graphModel * graph)
+ {
+ m_length = graph->length();
+ m_samples = new float[m_length];
+ memcpy(m_samples, graph->samples(), m_length * sizeof(float));
+ }
+ inline void copyFrom(const graphModel * graph)
+ {
+ memcpy(m_samples, graph->samples(), m_length * sizeof(float));
+ }
+ ~WaveSample()
+ {
+ delete [] m_samples;
+ }
+ inline void setInterpolate(bool _interpolate) { m_interpolate = _interpolate; }
+ float *m_samples;
+ int m_length;
+ bool m_interpolate;
+};
+
+class ExprSynth
+{
+ MM_OPERATORS
+public:
+ 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);
+ virtual ~ExprSynth();
+
+ void renderOutput(fpp_t frames, sampleFrame* buf );
+
+
+private:
+ ExprFront *m_exprO1, *m_exprO2;
+ const WaveSample *m_W1, *m_W2, *m_W3;
+ unsigned int m_note_sample;
+ unsigned int m_note_rel_sample;
+ float m_note_sample_sec;
+ float m_note_rel_sec;
+ float m_frequency;
+ float m_released;
+ NotePlayHandle* m_nph;
+ const sample_rate_t m_sample_rate;
+ const FloatModel *m_pan1,*m_pan2;
+ float m_rel_transition;
+ float m_rel_inc;
+
+} ;
+
+
+
+inline float positiveFraction(float x)
+{
+ if (std::isnan(x) || std::isinf(x))
+ return 0;
+ if (x<0)
+ {
+ x+=static_cast(1-x);
+ }
+ return x-static_cast(x);
+}
+
+template
+inline void clearArray(T* arr,unsigned int size)
+{
+ const T* const arr_end = arr + size;
+ while(arr < arr_end)
+ {
+ *arr=0;
+ ++arr;
+ }
+}
+
+
+
+#endif /* EXPRFRONT_H_ */
diff --git a/plugins/xpressive/exprtk.hpp b/plugins/xpressive/exprtk.hpp
new file mode 100644
index 000000000..6b766cebf
--- /dev/null
+++ b/plugins/xpressive/exprtk.hpp
@@ -0,0 +1,37969 @@
+/*
+ ******************************************************************
+ * C++ Mathematical Expression Toolkit Library *
+ * *
+ * Author: Arash Partow (1999-2017) *
+ * URL: http://www.partow.net/programming/exprtk/index.html *
+ * *
+ * Copyright notice: *
+ * Free use of the C++ Mathematical Expression Toolkit Library is *
+ * permitted under the guidelines and in accordance with the most *
+ * current version of the MIT License. *
+ * http://www.opensource.org/licenses/MIT *
+ * *
+ * Example expressions: *
+ * (00) (y + x / y) * (x - y / x) *
+ * (01) (x^2 / sin(2 * pi / y)) - x / 2 *
+ * (02) sqrt(1 - (x^2)) *
+ * (03) 1 - sin(2 * x) + cos(pi / y) *
+ * (04) a * exp(2 * t) + c *
+ * (05) if(((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) *
+ * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x *
+ * (07) z := x + sin(2 * pi / y) *
+ * (08) u := 2 * (pi * z) / (w := x + cos(y / pi)) *
+ * (09) clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1) *
+ * (10) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0) *
+ * (11) (2sin(x)cos(2y)7 + 1) == (2 * sin(x) * cos(2*y) * 7 + 1) *
+ * (12) (x ilike 's*ri?g') and [y < (3 z^7 + w)] *
+ * *
+ ******************************************************************
+*/
+
+
+#ifndef INCLUDE_EXPRTK_HPP
+#define INCLUDE_EXPRTK_HPP
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include