Xpressive plugin (#3259)
* First Preview of the X-Pressive Plugin (exprtk.hpp is not included, get it from my exprtk fork in the branch internal_data_functions) available keys: f- note's frequency. available only in the output expressions t- time in seconds. in the Waves (W1,W2,W3) it's in the range [0,1) key- the note's keyboard key. available only in the output expressions. v- the note's velocity (divided by 255.0 so it is in the range [0,1]). available only in the output expressions. rel- gives 0 while the key is holded, and 1 after the key release. available only in the output expressions. A1,A2,A3- general purpose knobs (you can control them with the automations). available only in the output expressions. W1,W2,W3- precalculated wave forms. can be also load from file. you can use them only in the output expressions available functions: cent(x)- gives pow(2,x/1200) rand()- random number generator. in range [-1,1). each call gives other value. randv(i)- random vector (with pseudo infinite integer cells). the values are in range [-1,1). it's stays consistent only across the note playback. so each note playback will get other vector (even on the same key). sinew- sine wave with period of 1. saww- saw wave with period of 1. squarew- square wave with period of 1. trianglew- triangle wave with period of 1. expw- exponent wave with period of 1. expnw- another exponent wave with period of 1. moogw- moog wave with period of 1. moogsaww- moog-saw wave with period of 1. you can use * % ^ / + - pow sin log pi etc. * Xpressive Plug-In: Added Release transition knob that control the "rel" variable. (the duration of transit from 0 to 1) Fixed some problems in the displays. (update display when changing A1,A2,A3, clear display with invalid expression. * X-Pressive Plug-In: Few more fixes Changed the callbacks in exprfront.cpp to be templated. Added help. Added ExprTk.hpp. some bug fixes (inf issues). Added integrate function. * Special version of ExprTk with modified license (BSL) for the LMMS project https://github.com/LMMS/lmms * Xpressive Plug-In- fixed some building errors. Added the "e" euler's constant. * Xpressive Plug-In - fix mingw64 issues * X-Pressive Plug-in: Added "trel" (time since release) variable. The integrate function can now have unlimited usage. Added selective interpolation per wave. Improved a little the random vector function. Some other improvements, code cleaning, etc... * Xpressive Plug-In: move clearGraph definition into Graph.cpp. fixed compilation errors. (oops..) * X-Pressive plug-in: updated presets names * X-Pressive plug-in added semitone function, added sample-rate variable * X-Pressive plug-in, code cleaning, changed the rendering function to achieve performace gain. * X-Pressive plug-in - fix the string counting function * X-Pressive plug-in - until somebody will find a better solution, exprtk.hpp is patched under the name exprtk.patched.hpp ... * X-Pressive plug-in - fix compiling errors. * X-Pressive plug-in - added patch file for exprtk.hpp, added last function that gives last calculated samples. moved ExprSynth to be with ExprFront for performance reasons. * X-Pressive plugin - moved the patched file back to the source tree, added .gitignore file.. * X-Pressive plugin - fix compilation error. (isnan isinf) * X-Pressive plugin - tried to fix embed.cpp problem, added new variable to the parser (tempo) * X-Pressive plugin - fixed cmake script * X-Pressive plugin - updated the license and the diff file. * Updates to ExprTk * Added return statement enable/disable via parser settings Added exprtk_disable_return_statement macro for disabling return statements and associated exceptions at the source code level. * X-Pressive plugin - updated CMakeLists.txt to use the correct flags on each platform. also added exprtk.hpp as a dependency for the patch command. Updated the exprtk diff file. * X-Pressive plugin - moved the enhanced features flag to the WIN64 installation. * X-Pressive plugin - another fix for CMakeLists.txt * Minor updates to ExprTk Updated multi-sub expression operator to return final sub-expression type. Updates to exprtk_disable_return_statement macro for disabling return statements and associated exceptions at the source code level. * X-Pressive plug-in - added try-block around exprtk calls and enabled the -fexceptions flag, so patch file is no longer needed. * X-Pressive plug-in - small fix in CMakeLists.txt * Update ExprTk to tip of branch. * X-Pressive plugin - added graph drawing feature.. * Updating exprtk.hpp to the latest upstream version
@@ -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})
|
||||
|
||||
|
||||
21
data/presets/X-Pressive/Accordion.xpf
Normal file
21
data/presets/X-Pressive/Ambition.xpf
Normal file
21
data/presets/X-Pressive/Baby Violin.xpf
Normal file
21
data/presets/X-Pressive/Bad Singer.xpf
Normal file
21
data/presets/X-Pressive/Cloud Bass.xpf
Normal file
21
data/presets/X-Pressive/Creature.xpf
Normal file
34
data/presets/X-Pressive/Dream.xpf
Normal file
21
data/presets/X-Pressive/Electric Shock.xpf
Normal file
21
data/presets/X-Pressive/Faded Colors.xpf
Normal file
21
data/presets/X-Pressive/Fat Flute.xpf
Normal file
21
data/presets/X-Pressive/Frog.xpf
Normal file
21
data/presets/X-Pressive/Horn.xpf
Normal file
21
data/presets/X-Pressive/Low Battery.xpf
Normal file
21
data/presets/X-Pressive/Piano-Gong.xpf
Normal file
21
data/presets/X-Pressive/Rubber Bass.xpf
Normal file
21
data/presets/X-Pressive/Space Echoes.xpf
Normal file
21
data/presets/X-Pressive/Speaker Swapper.xpf
Normal file
21
data/presets/X-Pressive/Toss.xpf
Normal file
21
data/presets/X-Pressive/Untuned Bell.xpf
Normal file
21
data/presets/X-Pressive/Vibrato.xpf
Normal file
21
data/presets/X-Pressive/X-Distorted.xpf
Normal file
@@ -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();
|
||||
|
||||
@@ -80,6 +80,7 @@ IF("${PLUGIN_LIST}" STREQUAL "")
|
||||
watsyn
|
||||
waveshaper
|
||||
vibed
|
||||
xpressive
|
||||
zynaddsubfx
|
||||
)
|
||||
|
||||
|
||||
2
plugins/xpressive/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#ignore the patched file
|
||||
exprtk.patched.hpp
|
||||
10
plugins/xpressive/CMakeLists.txt
Normal file
@@ -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")
|
||||
|
||||
17
plugins/xpressive/MIT
Normal file
@@ -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.
|
||||
BIN
plugins/xpressive/artwork.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
888
plugins/xpressive/expressive_plugin.cpp
Normal file
@@ -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 <QDomElement>
|
||||
|
||||
#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<ExprSynth*>(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<ExprSynth *>(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<Expressive>();
|
||||
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<Expressive>();
|
||||
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<Expressive>();
|
||||
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<Expressive>();
|
||||
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<Expressive>();
|
||||
|
||||
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<Expressive>();
|
||||
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=
|
||||
"<b>O1, O2</b> - Two output waves. Panning is controled by PN1 and PN2.<br>"
|
||||
"<b>W1, W2, W3</b> - Wave samples evaluated by expression. In these samples, t variable ranges [0,1).<br>"
|
||||
"These waves can be used as functions inside the output waves (O1, O2). The wave period is 1.<br>"
|
||||
"<h4>Available variables:</h4><br>"
|
||||
"<b>t</b> - Time in seconds.<br>"
|
||||
"<b>f</b> - Note's pitched frequency. Available only in the output expressions.<br>"
|
||||
"<b>key</b> - Note's keyboard key. 0 denotes C0, 48 denotes C4, 96 denotes C8. Available only in the output expressions.<br>"
|
||||
"<b>bnote</b> - Base note. By default it is 57 which means A5, unless you change it.<br>"
|
||||
"<b>srate</b> - Sample rate. In wave expression it returns the wave's number of samples.<br>"
|
||||
"<b>tempo</b> - Song's Tempo. Available only in the output expressions.<br>"
|
||||
"<b>v</b> - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.<br>"
|
||||
"<b>rel</b> - Gives 0.0 while the key is holded, and 1.0 after the key release. Available only in the output expressions.<br>"
|
||||
"<b>trel</b> - Time after release. While the note is holded, it gives 0.0. Afterwards, it start counting seconds.<br>"
|
||||
"The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob<br>"
|
||||
"<b>A1, A2, A3</b> - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].<br>"
|
||||
"<h4>Available functions:</h4><br>"
|
||||
"<b>W1, W2, W3</b> - As mentioned before. You can reference them only in O1 and O2.<br>"
|
||||
"<b>cent(x)</b> - Gives pow(2,x/1200), so you can multiply it with the f variable to pitch the frequency.<br>"
|
||||
"100 cents equals one semitone<br>"
|
||||
"<b>semitone(x)</b> - Gives pow(2,x/12), so you can multiply it with the f variable to pitch the frequency.<br>"
|
||||
"<b>last(n)</b> - Gives you the last n'th evaluated sample. The argument n must be in the range [1,500], or else, it will return 0.<br>"
|
||||
"<b>integrate(x)</b> - Integrates x by delta t (It sums values and divides them by sample rate).<br>"
|
||||
"If you use notes with automated frequency, you should use:<br>"
|
||||
"sinew(integrate(f)) instead of sinew(t*f)<br>"
|
||||
"<b>randv(x)</b> - A random vector. Each cell is reference by an integer index in the range [0,2^31]<br>"
|
||||
"Each evaluation of an expression results in different random vector.<br>"
|
||||
"Although, it remains consistent in the lifetime of a single wave.<br>"
|
||||
"If you want a single random values you can use randv(0),randv(1)... <br>"
|
||||
"and every reference to randv(a) will give you the same value."
|
||||
"If you want a random wave you can use randv(t*srate).<br>"
|
||||
"Each random value is in the range [-1,1).<br>"
|
||||
"<b>sinew(x)</b> - A sine wave with period of 1 (In contrast to real sine wave which have a period of 2*pi).<br>"
|
||||
"<b>trianglew(x)</b> - A triangle wave with period of 1.<br>"
|
||||
"<b>squarew(x)</b> - A square wave with period of 1.<br>"
|
||||
"<b>saww(x)</b> - A saw wave with period of 1.<br>"
|
||||
"<b>clamp(min_val,x,max_val)</b> - 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.<br>"
|
||||
"<b>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.</b><br>"
|
||||
"<b>Operands + - * / % ^ > < >= <= == != & | are also available.</b><br>"
|
||||
"<b>Amplitude Modulation</b> - W1(t*f)*(1+W2(t*f))<br>"
|
||||
"<b>Ring Modulation</b> - W1(t * f)*W2(t * f)<br>"
|
||||
"<b>Mix Modulation</b> - 0.5*( W1(t * f) + W2(t * f) )<br>"
|
||||
"<b>Frequency Modulation</b> - [vol1]*W1( integrate( f + srate*[vol2]*W2( integrate(f) ) ) )<br>"
|
||||
"<b>Phase Modulation</b> - [vol1]*W1( integrate(f) + [vol2]*W2( integrate(f) ) )<br>"
|
||||
;
|
||||
|
||||
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<InstrumentTrack *>(_data)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
227
plugins/xpressive/expressive_plugin.h
Normal file
@@ -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 <QPlainTextEdit>
|
||||
|
||||
#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
|
||||
792
plugins/xpressive/exprsynth.cpp
Normal file
@@ -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 <string>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include <cstdlib>
|
||||
#include <random>
|
||||
|
||||
#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<float> symbol_table_t;
|
||||
typedef exprtk::expression<float> expression_t;
|
||||
typedef exprtk::parser<float> parser_t;
|
||||
|
||||
template <typename T,typename Functor,bool optimize>
|
||||
struct freefunc0 : public exprtk::ifunction<T>
|
||||
{
|
||||
using exprtk::ifunction<T>::operator();
|
||||
|
||||
freefunc0() : exprtk::ifunction<T>(0) {
|
||||
if (optimize) { exprtk::disable_has_side_effects(*this); }
|
||||
}
|
||||
inline T operator()()
|
||||
{ return Functor::process(); }
|
||||
};
|
||||
template <typename T,typename Functor,bool optimize>
|
||||
struct freefunc1 : public exprtk::ifunction<T>
|
||||
{
|
||||
using exprtk::ifunction<T>::operator();
|
||||
|
||||
freefunc1() : exprtk::ifunction<T>(1) {
|
||||
if (optimize) { exprtk::disable_has_side_effects(*this); }
|
||||
}
|
||||
inline T operator()(const T& x)
|
||||
{ return Functor::process(x); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IntegrateFunction : public exprtk::ifunction<T>
|
||||
{
|
||||
|
||||
using exprtk::ifunction<T>::operator();
|
||||
virtual ~IntegrateFunction()
|
||||
{
|
||||
delete [] m_counters;
|
||||
}
|
||||
|
||||
IntegrateFunction(const unsigned int* frame, unsigned int sample_rate,unsigned int max_counters) :
|
||||
exprtk::ifunction<T>(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 <typename T>
|
||||
struct LastSampleFunction : public exprtk::ifunction<T>
|
||||
{
|
||||
|
||||
using exprtk::ifunction<T>::operator();
|
||||
virtual ~LastSampleFunction()
|
||||
{
|
||||
delete [] m_samples;
|
||||
}
|
||||
|
||||
LastSampleFunction(unsigned int history_size) :
|
||||
exprtk::ifunction<T>(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 <typename T>
|
||||
struct WaveValueFunction : public exprtk::ifunction<T>
|
||||
{
|
||||
using exprtk::ifunction<T>::operator();
|
||||
|
||||
WaveValueFunction(const T* v, std::size_t s)
|
||||
: exprtk::ifunction<T>(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 <typename T>
|
||||
struct WaveValueFunctionInterpolate : public exprtk::ifunction<T>
|
||||
{
|
||||
using exprtk::ifunction<T>::operator();
|
||||
|
||||
WaveValueFunctionInterpolate(const T* v, std::size_t s)
|
||||
: exprtk::ifunction<T>(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<float>
|
||||
{
|
||||
using exprtk::ifunction<float>::operator();
|
||||
|
||||
RandomVectorFunction(const unsigned int seed) :
|
||||
exprtk::ifunction<float>(1),
|
||||
m_rseed(seed)
|
||||
{ exprtk::disable_has_side_effects(*this); }
|
||||
|
||||
inline float operator()(const float& index)
|
||||
{
|
||||
if (index < 0 || std::isnan(index) || std::isinf(index))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const unsigned int xi = (unsigned int)index;
|
||||
const unsigned int si = m_rseed % data_size;
|
||||
const unsigned int sa = m_rseed / data_size;
|
||||
unsigned int res=rotateLeft(random_data[(xi + si) % data_size] ^ random_data[(xi / data_size + sa) % data_size],sa % 31 + 1);
|
||||
res ^= rotateLeft(random_data[(3 * xi + si) % data_size] ^ random_data[(xi / data_size + 2 * sa) % data_size],xi % 31 + 1);
|
||||
return static_cast<int>(res) / (float)(1 << 31);
|
||||
}
|
||||
|
||||
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<float> dist(-1.0f, 1.0f);
|
||||
struct float_random_with_engine
|
||||
{
|
||||
static inline float process()
|
||||
{
|
||||
return dist(generator);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static freefunc0<float,SimpleRandom::float_random_with_engine,false> 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<WaveValueFunction<float>* > m_cyclics;
|
||||
std::vector<WaveValueFunctionInterpolate<float>* > m_cyclics_interp;
|
||||
RandomVectorFunction m_rand_vec;
|
||||
IntegrateFunction<float> *m_integ_func;
|
||||
LastSampleFunction<float> m_last_func;
|
||||
};
|
||||
|
||||
|
||||
struct sin_wave
|
||||
{
|
||||
static inline float process(float x)
|
||||
{
|
||||
x = positiveFraction(x);
|
||||
return sinf(x * F_2PI);
|
||||
}
|
||||
};
|
||||
static freefunc1<float,sin_wave,true> 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<float,square_wave,true> 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<float,triangle_wave,true> triangle_wave_func;
|
||||
struct saw_wave
|
||||
{
|
||||
static inline float process(float x)
|
||||
{
|
||||
x=positiveFraction(x);
|
||||
return 2.0f * x - 1.0f;
|
||||
}
|
||||
};
|
||||
static freefunc1<float,saw_wave,true> 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<float,moogsaw_wave,true> 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<float,moog_wave,true> 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<float,exp_wave,true> 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<float,exp2_wave,true> exp2_wave_func;
|
||||
struct harmonic_cent
|
||||
{
|
||||
static inline float process(float x)
|
||||
{
|
||||
return powf(2, x / 1200);
|
||||
}
|
||||
};
|
||||
static freefunc1<float,harmonic_cent,true> harmonic_cent_func;
|
||||
struct harmonic_semitone
|
||||
{
|
||||
static inline float process(float x)
|
||||
{
|
||||
return powf(2, x / 12);
|
||||
}
|
||||
};
|
||||
static freefunc1<float,harmonic_semitone,true> 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<float> *wvf = new WaveValueFunctionInterpolate<float>(data, length);
|
||||
m_data->m_cyclics_interp.push_back(wvf);
|
||||
return m_data->m_symbol_table.add_function(name, *wvf);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaveValueFunction<float> *wvf = new WaveValueFunction<float>(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<float>(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<float> * last_func1 = &m_exprO1->getData()->m_last_func;
|
||||
LastSampleFunction<float> * 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;
|
||||
}
|
||||
}
|
||||
141
plugins/xpressive/exprsynth.h
Normal file
@@ -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 <cmath>
|
||||
#include <cstddef>
|
||||
#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<int>(1-x);
|
||||
}
|
||||
return x-static_cast<int>(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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_ */
|
||||
37969
plugins/xpressive/exprtk.hpp
Normal file
BIN
plugins/xpressive/help_active.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
plugins/xpressive/help_inactive.png
Normal file
|
After Width: | Height: | Size: 806 B |
BIN
plugins/xpressive/logo.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
plugins/xpressive/o1_active.png
Normal file
|
After Width: | Height: | Size: 629 B |
BIN
plugins/xpressive/o1_inactive.png
Normal file
|
After Width: | Height: | Size: 508 B |
BIN
plugins/xpressive/o2_active.png
Normal file
|
After Width: | Height: | Size: 723 B |
BIN
plugins/xpressive/o2_inactive.png
Normal file
|
After Width: | Height: | Size: 602 B |
BIN
plugins/xpressive/w1_active.png
Normal file
|
After Width: | Height: | Size: 685 B |
BIN
plugins/xpressive/w1_inactive.png
Normal file
|
After Width: | Height: | Size: 549 B |
BIN
plugins/xpressive/w2_active.png
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
plugins/xpressive/w2_inactive.png
Normal file
|
After Width: | Height: | Size: 628 B |
BIN
plugins/xpressive/w3_active.png
Normal file
|
After Width: | Height: | Size: 771 B |
BIN
plugins/xpressive/w3_inactive.png
Normal file
|
After Width: | Height: | Size: 626 B |
BIN
plugins/xpressive/wavegraph.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
@@ -130,14 +130,8 @@ void Graph::mouseMoveEvent ( QMouseEvent * _me )
|
||||
x = qMax( 2, qMin( x, width()-3 ) );
|
||||
y = qMax( 2, qMin( y, height()-3 ) );
|
||||
|
||||
if( qAbs( diff ) > 1 )
|
||||
{
|
||||
drawLineAt( x, y, m_lastCursorX );
|
||||
}
|
||||
else
|
||||
{
|
||||
changeSampleAt( x, y );
|
||||
}
|
||||
|
||||
drawLineAt( x, y, m_lastCursorX );
|
||||
|
||||
// update mouse
|
||||
if( diff != 0 )
|
||||
@@ -209,25 +203,47 @@ void Graph::drawLineAt( int _x, int _y, int _lastx )
|
||||
|
||||
float range = minVal - maxVal;
|
||||
float val = ( _y*range/( height()-5 ) ) + maxVal;
|
||||
float lastval = model() -> m_samples[ (int)( _lastx * xscale ) ];
|
||||
|
||||
// calculate line drawing variables
|
||||
int linelen = qAbs( _x - _lastx ) + 1;
|
||||
int xstep = _x > _lastx ? -1 : 1;
|
||||
float ystep = ( lastval - val ) / linelen;
|
||||
|
||||
int start = INT_MAX;
|
||||
int end = 0;
|
||||
// draw a line
|
||||
for ( int i = 0; i < linelen; i++ )
|
||||
|
||||
int sample_begin, sample_end;
|
||||
float lastval;
|
||||
float val_begin, val_end;
|
||||
|
||||
if (_lastx > _x)
|
||||
{
|
||||
int x = (_x + (i * xstep)) * xscale; // get x value
|
||||
model()->drawSampleAt( x, val + (i * ystep));
|
||||
start = qMin( start, x );
|
||||
end = qMax( end, x );
|
||||
sample_begin = (int)((_x) * xscale);
|
||||
sample_end = (int)ceil((_lastx+1) * xscale);
|
||||
lastval = model() -> m_samples[ (int)( sample_end - 1 ) ];
|
||||
val_begin = val;
|
||||
val_end = lastval;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sample_begin = (int)(_lastx * xscale);
|
||||
sample_end = (int)ceil((_x+1) * xscale);
|
||||
lastval = model() -> m_samples[ (int)( sample_begin ) ];
|
||||
val_begin = lastval;
|
||||
val_end = val;
|
||||
|
||||
}
|
||||
|
||||
model()->samplesChanged( start, end );
|
||||
// calculate line drawing variables
|
||||
int linelen = sample_end - sample_begin;
|
||||
if (linelen == 1)
|
||||
{
|
||||
val_begin = val;
|
||||
}
|
||||
//int xstep = _x > _lastx ? -1 : 1;
|
||||
float ystep = ( val_end - val_begin ) / linelen;
|
||||
|
||||
// draw a line
|
||||
for ( int i = 0 ; i < linelen; i++ )
|
||||
{
|
||||
model()->drawSampleAt( sample_begin + i , val_begin + ((i ) * ystep));
|
||||
}
|
||||
|
||||
|
||||
model()->samplesChanged( sample_begin, sample_end );
|
||||
}
|
||||
|
||||
void Graph::changeSampleAt( int _x, int _y )
|
||||
@@ -265,6 +281,7 @@ void Graph::mouseReleaseEvent( QMouseEvent * _me )
|
||||
m_mouseDown = false;
|
||||
setCursor( Qt::CrossCursor );
|
||||
update();
|
||||
emit drawn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +650,24 @@ void graphModel::smoothNonCyclic()
|
||||
emit samplesChanged(0, length()-1);
|
||||
}
|
||||
|
||||
|
||||
//makes a cyclic convolution.
|
||||
void graphModel::convolve(const float *convolution, const int convolutionLength, const int centerOffset)
|
||||
{
|
||||
// store values in temporary array
|
||||
QVector<float> temp = m_samples;
|
||||
const int graphLength = length();
|
||||
float sum;
|
||||
for ( int i = 0; i < graphLength; i++ )
|
||||
{
|
||||
sum = 0;
|
||||
for ( int j = 0; j < convolutionLength; j++ )
|
||||
{
|
||||
sum += convolution[j] * temp[( i + j ) % graphLength];
|
||||
}
|
||||
m_samples[( i + centerOffset ) % graphLength] = sum;
|
||||
}
|
||||
emit samplesChanged(0, graphLength - 1);
|
||||
}
|
||||
|
||||
void graphModel::normalize()
|
||||
{
|
||||
@@ -676,7 +710,7 @@ void graphModel::invert()
|
||||
void graphModel::shiftPhase( int _deg )
|
||||
{
|
||||
// calculate offset in samples
|
||||
int offset = ( _deg * length() ) / 360; //multiply first because integers
|
||||
const int offset = ( _deg * length() ) / 360; //multiply first because integers
|
||||
|
||||
// store values in temporary array
|
||||
QVector<float> temp = m_samples;
|
||||
@@ -692,6 +726,14 @@ void graphModel::shiftPhase( int _deg )
|
||||
emit samplesChanged( 0, length()-1 );
|
||||
}
|
||||
|
||||
void graphModel::clear()
|
||||
{
|
||||
const int graph_length = length();
|
||||
for( int i = 0; i < graph_length; i++ )
|
||||
m_samples[i] = 0;
|
||||
emit samplesChanged( 0, graph_length - 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void graphModel::drawSampleAt( int x, float val )
|
||||
|
||||